--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Commit e5756d7ab065475a18acdcafc008a767fcff87e0
Parents : a40a27c
Author : Ivan <e46112d44649266d71fe2193e00a4710>
Signature : T66BB85Valid, signed by author
Date : 2026-07-22T10:32:36-05:00
feat: various updates to filesync, ui, deps, add active sessions in about page and stuff related to new rns 1.4.0
Changes
92 files changed, 10063 insertions(+), 3830 deletions(-)
Diff
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24ff5abb..e6fb188e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,17 +10,27 @@ All notable changes to this project will be documented in this file.
- Bundled Bug Reports plugin and plugin i18n / UI slots / contribution registries
- Map overlays from NomadNet and RNGit (KMZ/KML/GeoJSON) with cache and refresh
- RNS Link WebSocket API for external apps (and plugin capabilities)
+- RNS File Sync for shared folders on the mesh, with an in-app file manager to browse, upload, download, and delete files in the sync folder
- Settings: Reticulum instance/share controls, tabbed Settings nav, desktop close/tray behavior
- Nomad favourites: per-identity section layout in the database
- Optional pip-rns / rngit install path for RNS packages and docs
- Message export and import with contacts and read state
+- Message maintenance in Settings: purge old local messages and clear duplicates
- Host battery status on About and in the header (Electron, Android, Chromium)
-- RSM signing and verification for meshchatx.rsm (CI and pre-commit resign)
+- System resource monitoring for CPU and memory in the UI
+- RSM signing and verification for meshchatx.rsm
- Notification sound settings
-- LXMFy 1.6.5 vendor refresh, wasmtime, mutation test tasks
+- LXMFy 2.0.1 vendor refresh with RRC hub client support for bots, plus wasmtime
- Network visualiser WebGL + WASM renderer (vis-network fallback) and Settings renderer preference
- Interfaces: internal mode, recursive path requests, announces-from-internal, discovery location command, and Backbone fast-flapping options (RNS 1.3.7 to 1.3.9)
-- Dependencies: **RNS** 1.4.0 and **LXMF** 1.1.0, with local propagation node controls for sequential stamp validation, static-peer bypass, max inbound syncs, transfer size reporting, and inbound delivery cancel
+- Reticulum interface module management from the UI
+- Reticulum 1.4.0 and LXMF 1.1.0, with propagation node options and cancel for incoming large message downloads from the header
+- Relay Chat room keys so hosts can require a key to join a room
+- Desktop privacy: Windows screen security to omit MeshChatX from screenshots, recording, and Recall
+- Android privacy options to block screenshots and clear the clipboard when backgrounded
+- Remote management allow-list for identities that may query this instance with rnstatus/rnpath
+- Post-install prompts for existing users after upgrades
+- Coolify-oriented Docker Compose with resource limits for deployments
### Changed
@@ -30,20 +40,19 @@ All notable changes to this project will be documented in this file.
- Conversation list API omits contact image blobs and caps unbounded callers (Map, Network Visualiser)
- Messages page conversation poll is slower and skips while the tab is hidden
- Schema v51 message-flag backfill skips empty databases so fresh init stays fast
+- Outbound message status icons and titles reflect delivery method and state more clearly
- Relay Chat: denser hub UI, announce interval, collapsed system lines, reconnect notices
- Relay Chat: clickable Nomad and LXMF links plus basic markdown for code, bold, italic, and strikethrough
- Low-memory cleanup and SQLite pragmas under memory pressure
-- CI benches use median-of-medians and quieter regression gates
-- Backend benchmarks cover slim conversation list, mark-as-read, call history, and missed-call notification paths
-- Benchmark gate fails when a full-suite run drops required benches or loses coverage vs baseline
-- Backend tests can run sharded in CI
+- Auto-resend keeps attachments and cleans duplicate outbound rows more reliably
+- Calls and audio work in Docker and headless setups via hostless LXST backends and the web audio bridge
+- Hardened identity path handling, stamp enforcement, plugin integrity, Nomad downloads, and local file path jails
- Plugin strings live in plugin bundles, not main locale files
- Docker frontend build installs Go, builds visualiser WASM, and fails if WASM artifacts are missing
### Fixed
-- SLSA provenance jobs compile the generic generator from source so attest no longer fails with a missing slsa-generator-generic-linux-amd64 binary
-- Android: lxmfy packaging, flock soft-lock, splash/logo clipping, emulator smoke, Landlock skipped on Android
+- Android: lxmfy packaging, flock soft-lock, splash/logo clipping, Landlock skipped on Android
- Android RNode BLE/USB via Chaquopy
- Startup check and disable unsupported interfaces
- Nomad favourites: no more Unknown Node / lost custom sections
@@ -51,7 +60,6 @@ All notable changes to this project will be documented in this file.
- Bots and RNSh work in frozen macOS/Windows builds
- Sensitive config no longer mutable over WebSocket. Reticulum config repair on startup
- Paper message URI encoding for non-ASCII title and content
-- Nightly releases and broader self-test / CI coverage
- LXMA contact import works with current RNS public-key loading and remembers the peer key before announce
- Android calls: overlay accept opens the phone tab so native audio attaches. Web-audio no longer permanently disabled after a bridge error
- Android Codec2: reliable libcodec2 preload, Gradle fails without Codec2 wheels or jniLibs, and unavailable Codec2 profiles are hidden
diff --git a/cogs/__init__.py b/cogs/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/cogs/basic.py b/cogs/basic.py
new file mode 100644
index 00000000..3895f4ae
--- /dev/null
+++ b/cogs/basic.py
@@ -0,0 +1,18 @@
+from lxmfy import Command
+
+
+class BasicCommands:
+ def __init__(self, bot):
+ self.bot = bot
+
+ @Command(name="hello", description="Says hello")
+ async def hello(self, ctx):
+ ctx.reply(f"Hello {ctx.sender}!")
+
+ @Command(name="about", description="About this bot")
+ async def about(self, ctx):
+ ctx.reply("I'm a bot created with LXMFy!")
+
+
+def setup(bot):
+ bot.add_cog(BasicCommands(bot))
diff --git a/config/cogs/__init__.py b/config/cogs/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/docs/agents/README.md b/docs/agents/README.md
index cd13fe4f..d034ba61 100644
--- a/docs/agents/README.md
+++ b/docs/agents/README.md
@@ -26,6 +26,7 @@ Optional editor rules (if present under `.cursor/rules/` or similar):
| [conventions/core.md](conventions/core.md) | Always-on standards |
| [conventions/frontend.md](conventions/frontend.md) | Vue UI |
| [conventions/backend.md](conventions/backend.md) | Python / HTTP / SQLite |
+| [conventions/path-jail.md](conventions/path-jail.md) | Local FS APIs: jail, symlinks, tests |
| [conventions/android.md](conventions/android.md) | Android WebView bridge |
| [conventions/tests.md](conventions/tests.md) | Test placement, oracles, verification |
@@ -62,6 +63,7 @@ Optional editor rules (if present under `.cursor/rules/` or similar):
| Skill | Use when |
| ------------------------------------------------------------------ | ------------------------------------------- |
| [auth-csrf-ws-security](skills/auth-csrf-ws-security/SKILL.md) | CSRF, auth, WS mutator denylist |
+| [path-jail-local-fs](skills/path-jail-local-fs/SKILL.md) | Local file browse/upload/delete path jails |
| [plugin-install-security](skills/plugin-install-security/SKILL.md) | Plugin install, RSG, permissions, integrity |
### Platforms and boot
diff --git a/docs/agents/conventions/backend.md b/docs/agents/conventions/backend.md
index e728ecd1..15e0ef89 100644
--- a/docs/agents/conventions/backend.md
+++ b/docs/agents/conventions/backend.md
@@ -11,3 +11,4 @@ Applies when editing `meshchatx/**/*.py`.
- Identity restore validates size and empty payloads. Preserve existing identity metadata on re-import.
- No backticks in code comments. Prefer plain words or quoted identifiers.
- RRC / LXMF / LXST changes: open the matching skill under `docs/agents/skills/` and run oracle-style tests when behaviour changes.
+- Local filesystem browse/upload/download/delete: follow `docs/agents/conventions/path-jail.md` and `docs/agents/skills/path-jail-local-fs/SKILL.md`.
diff --git a/docs/agents/conventions/path-jail.md b/docs/agents/conventions/path-jail.md
new file mode 100644
index 00000000..95d09cdf
--- /dev/null
+++ b/docs/agents/conventions/path-jail.md
@@ -0,0 +1,37 @@
+# Path jail for local filesystem APIs
+
+Applies when adding or changing HTTP or handler code that lists, reads, writes, uploads, downloads, or deletes files under identity storage or a feature root.
+
+## Default threat model
+
+Treat MeshChatX local API access (UI session, shared host, scripted client) as already compromised for the purpose of path checks. The attacker must still fail to:
+
+- Escape the feature root with `../`, absolute paths, null bytes, or Windows separators
+- Reach sibling identity storage or host paths outside the active identity
+- Touch reserved tops (`identity`, `lxmf`, `database.db`, bots, plugins, backups, keys)
+- Follow symlinks that point outside the jail
+- Exfil via download or content endpoints that skip the same resolve helper
+- Upload filenames that smuggle path segments or reserved sidecars
+
+## Hard rules
+
+1. One dedicated resolve helper per feature root (example: sync-root only for FileSync manager). Do not reuse a looser picker jail for CRUD.
+2. Normalize then `realpath`. Success paths must equal the root or start with `root + sep`.
+3. Fail closed: generic error, HTTP 400 for bad input, never 500 for jail rejects.
+4. Upload: sanitize to basename only. Join under a resolved parent. Cap size.
+5. Delete: refuse the root itself. Default to file or empty directory only unless recursive delete is explicit and tested.
+6. Skip and refuse mutation of dotfiles and protocol sidecars (for FileSync: `.rns-filesync*`, `.rns-xfer*`).
+7. Mutating routes stay CSRF-protected HTTP via `window.api`. No WS mutators for file CRUD.
+8. Keep identity-scoped state. Never browse or mutate another identity storage path.
+
+## Tests required
+
+- Adversarial traversal and absolute-path cases with bait files that must survive
+- Cross-identity bait directory in the same test
+- Symlink-out cases (POSIX) for list, read, write, delete
+- Oracle or Hypothesis: accept only when resolved path stays under the root
+- Frontend mutators go through `window.api` (apiFetchGuard stays green)
+
+Full workflow: `docs/agents/skills/path-jail-local-fs/SKILL.md`.
+Reference implementation: `meshchatx/src/backend/rns_filesync_handler.py` (`_resolve_manager_path` and manager APIs).
+Oracle examples: `tests/backend/test_rns_filesync_security.py`, `tests/backend/test_path_jail_oracles.py`.
diff --git a/docs/agents/conventions/tests.md b/docs/agents/conventions/tests.md
index 170ec1c5..098de001 100644
--- a/docs/agents/conventions/tests.md
+++ b/docs/agents/conventions/tests.md
@@ -28,6 +28,7 @@ Prefer:
- Round-trip or shape invariants when the API is pure parsing
Full skill: `docs/agents/skills/test-oracles/SKILL.md`.
+Path jail filesystem features: `docs/agents/skills/path-jail-local-fs/SKILL.md` and `docs/agents/conventions/path-jail.md`.
Exploratory bug hunting: `docs/agents/skills/exploratory-testing/SKILL.md`.
## Extended Edge Case Tester (EECT) and Live Validation (LV)
diff --git a/docs/agents/overview.md b/docs/agents/overview.md
index 484ba98c..78414366 100644
--- a/docs/agents/overview.md
+++ b/docs/agents/overview.md
@@ -205,6 +205,8 @@ Prefer bind `127.0.0.1`, HTTPS, and auth if other local users share the host.
Sensitive config changes (for example auth enable / password hash) must use CSRF-protected HTTP endpoints, not unrestricted WebSocket mutators.
+Local filesystem browse/upload/download/delete APIs must path-jail to a feature or identity root. See `docs/agents/conventions/path-jail.md` and `docs/agents/skills/path-jail-local-fs/SKILL.md`.
+
Password reset: `--reset-password` or `MESHCHAT_RESET_PASSWORD=true` clears the stored hash so a new password can be set in the UI.
### Plugins
diff --git a/docs/agents/skills/lxmf-messaging/SKILL.md b/docs/agents/skills/lxmf-messaging/SKILL.md
index ecc9b1a6..0cbf7133 100644
--- a/docs/agents/skills/lxmf-messaging/SKILL.md
+++ b/docs/agents/skills/lxmf-messaging/SKILL.md
@@ -15,13 +15,31 @@ LXMF is store-and-forward mail on Reticulum. Do not require clearnet, DNS, or a
## Key paths
-| Area | Path |
-| ------------------------ | --------------------------------------------------------- |
-| Identity / router wiring | `meshchatx/src/backend/identity_context.py` |
-| Message handler | `meshchatx/src/backend/message_handler.py` (and related) |
-| HTTP/WS surface | `meshchatx/meshchat.py` |
-| Frontend conversations | `meshchatx/src/frontend/components/` conversation viewers |
-| Config | config managers / settings UI for LXMF options |
+| Area | Path |
+| ------------------------ | -------------------------------------------------------------------------------------------------- |
+| Identity / router wiring | `meshchatx/src/backend/identity_context.py` |
+| Message handler | `meshchatx/src/backend/message_handler.py` (and related) |
+| HTTP/WS surface | `meshchatx/meshchat.py` |
+| Inbound cancel helpers | `meshchatx/src/backend/meshchat_utils.py` (`list_inbound_deliveries`, `cancel_inbound_deliveries`) |
+| Frontend conversations | `meshchatx/src/frontend/components/` conversation viewers |
+| Config | config managers / settings UI for LXMF options |
+
+## LXMF 1.1 / RNS 1.4 inbound cancel
+
+Large inbound LXMF deliveries use RNS Resources. LXMF exposes:
+
+- `LXMRouter.inbound_resources()` / `inbound_count()`
+- `cancel_inbound(resource_hash)` and `cancel_all_inbound()`
+
+MeshChatX surfaces them as:
+
+- Status: `inbound_delivery_count` and `inbound_deliveries` on `/api/v1/lxmf/propagation-node/status`
+- Cancel: `POST /api/v1/lxmf/propagation-node/cancel-inbound` with optional `{ "resource_hash": "..." }`
+- Header UI in `App.vue` when active inbound transfers exist
+
+Outbound cancel remains `POST /api/v1/lxmf-messages/{hash}/cancel` via `cancel_outbound`.
+
+Keep minimum versions: `rns>=1.4.0`, `lxmf>=1.1.0`.
## Gates
diff --git a/docs/agents/skills/path-jail-local-fs/SKILL.md b/docs/agents/skills/path-jail-local-fs/SKILL.md
new file mode 100644
index 00000000..1ac5765a
--- /dev/null
+++ b/docs/agents/skills/path-jail-local-fs/SKILL.md
@@ -0,0 +1,118 @@
+# Skill: path-jail-local-fs
+
+Add or change local filesystem features (browse, upload, download, mkdir, delete) with a hard path jail, CSRF-safe mutators, and oracle-style tests. Do not ship file CRUD that can escape identity or feature roots.
+
+## When to use
+
+- New in-app file managers, uploads, downloads, or directory browsers
+- Extending FileSync, RNCP, page-nodes files, docs packs, repo wheels, or similar storage APIs
+- Any handler that takes a client path or multipart filename and touches disk
+- Security review of existing list/read/write/delete file endpoints
+
+Also read:
+
+- `docs/agents/conventions/path-jail.md`
+- `docs/agents/skills/auth-csrf-ws-security/SKILL.md`
+- `docs/agents/skills/test-oracles/SKILL.md`
+- `docs/agents/skills/page-toast-tests/SKILL.md` when adding UI
+
+## Threat model (assume without asking)
+
+Caller already has MeshChatX HTTP API access. They must not list, read, write, or delete outside the configured feature root. That includes host home dirs, `/etc`, other identities, and reserved identity-storage tops.
+
+You do not need the user to restate path jail, symlink policy, CSRF, or bait-file tests. Apply them by default.
+
+## Workflow
+
+### 1) Choose the jail root
+
+Pick the tightest correct root for the feature:
+
+| Feature shape | Jail root |
+| -------------------------------------- | --------------------------------------------- |
+| FileSync in-app manager | Configured `sync_directory` only |
+| Folder picker for choosing a sync root | Identity storage with reserved tops blocked |
+| RNCP received / shared | That feature directory under identity storage |
+| Page-node files | That node file directory |
+
+Never use a looser picker jail for tree/upload/delete/content of a tighter feature.
+
+### 2) One resolve helper
+
+Add something like `_resolve_<feature>_path(...)` that:
+
+1. Takes relative client paths only (reject absolute / drive / UNC / null bytes)
+2. Uses vendor or shared normalize helpers when available (`normalize_relpath`, `resolve_under_root`)
+3. Rejects forbidden names (dotfiles, protocol sidecars)
+4. `realpath` membership: equal root or `root + sep` prefix
+5. Symlinks: after realpath still inside root. Prefer rejecting symlink entries for write/delete/content
+6. Returns fail-closed errors with generic messages (no out-of-jail path reflection)
+
+Reference: `RnsFilesyncHandler._resolve_manager_path` in `meshchatx/src/backend/rns_filesync_handler.py`.
+
+### 3) Wire APIs
+
+Typical surface:
+
+| Method | Role |
+| ------------- | --------------------------------------------------------------------------------- |
+| GET tree/list | Browse under jail (works even if a mesh service is stopped when state is on disk) |
+| POST mkdir | Relative path under jail |
+| POST upload | Multipart. Optional subdir. Basename only. Size cap |
+| DELETE entry | File or empty dir by default |
+| GET content | Stream only after jail pass |
+
+HTTP: 400 on jail failure / bad input. Register routes in `tests/backend/fixtures/http_api_routes.json`. Add JSON GET contracts or exclude binary download routes in `http_api_response_registry.py`. Add mutating samples to EECT auth surface when relevant.
+
+### 4) Frontend
+
+- Relative paths only in the UI. Never send host absolute paths for CRUD.
+- Mutators via `window.api` / FormData so CSRF attaches.
+- Confirm before delete. Toasts via `ToastUtils`. User strings via i18n.
+- Keep OS "open folder" as optional convenience, not the only management path.
+
+### 5) Mandatory tests
+
+Extend or add:
+
+- Happy-path CRUD under the root (including stopped-service disk CRUD when applicable)
+- `_TRAVERSAL_PAYLOADS`-style rejects for tree, content, delete, mkdir, upload subdir
+- Absolute path to bait file outside storage (must survive)
+- Reserved tops under identity storage
+- Second identity storage bait in the same test
+- Symlink inside root pointing outside (list/read/write/delete fail closed)
+- Upload basename sanitization (path segments stripped or rejected, never escape)
+- Hypothesis or explicit oracle: accept only when resolved path stays under root
+- Frontend: mock `window.api`, assert upload/delete calls and toasts
+
+Soft fuzz that only checks "did not crash" is not enough. See `test-oracles`.
+
+## Stupid crap to refuse
+
+- Reusing identity-storage browse APIs as the file-manager base for a tighter root
+- Trusting `Content-Disposition` or multipart filenames as full save paths
+- Recursive delete of arbitrary trees without an explicit, tested flag
+- New WebSocket mutators for file upload/delete
+- Logging full absolute paths of failed escapes into user-visible errors
+- Cross-identity caches or shared temp dirs for uploads
+
+## Verification
+
+```bash
+task test:filesync:security
+uv run pytest tests/backend/test_path_jail_oracles.py -q --tb=short
+pnpm exec vitest run tests/frontend/apiFetchGuard.test.js
+```
+
+Adjust pytest and vitest paths to the feature you touched. Prefer `task` targets when they exist.
+
+## Finish gate
+
+Do not ship until all are true:
+
+1. Every list/read/write/delete/upload goes through the feature resolve helper
+2. Escape payloads never read or delete bait files outside the root
+3. Symlink-out and cross-identity cases fail closed with tests
+4. Oracle or Hypothesis coverage exists for the resolve helper
+5. Mutators are CSRF HTTP via `window.api`
+6. Route fixture / JSON contract registry updated when routes change
diff --git a/docs/en/messaging.md b/docs/en/messaging.md
index 64bd5054..9a8df6c0 100644
--- a/docs/en/messaging.md
+++ b/docs/en/messaging.md
@@ -105,6 +105,7 @@ WebSocket lxmf_message event on recipient UI
- Enable **auto-announce** so your `lxmf.delivery` aspect stays visible on the mesh.
- Check **Interfaces** if messages stall. No path to the peer means LXMF cannot deliver.
- Review stamp settings before joining busy public meshes.
+- While a large message is downloading, the header shows **Cancel incoming**. That stops active LXMF delivery resource transfers (`cancel_all_inbound` / per-resource cancel). Outbound send cancel stays on each message menu.
## See also
diff --git a/electron/loading.html b/electron/loading.html
index 3aae7aa8..ff9ba902 100644
--- a/electron/loading.html
+++ b/electron/loading.html
@@ -33,9 +33,7 @@
class="overflow-hidden rounded-2xl border border-slate-200/80 bg-white/90 shadow-lg shadow-slate-200/50 backdrop-blur-sm dark:border-zinc-700/80 dark:bg-zinc-900/90 dark:shadow-black/40"
>
<div class="px-6 pt-8 pb-2 text-center">
- <div
- class="mx-auto mb-4 flex h-20 w-20 items-center justify-center overflow-visible rounded-2xl bg-white p-2 shadow-inner ring-1 ring-slate-200/80 dark:bg-zinc-950 dark:ring-zinc-700"
- >
+ <div class="mx-auto mb-4 flex h-20 w-20 items-center justify-center overflow-visible">
<img class="h-14 w-14 object-contain" src="./assets/images/logo.png" alt="" />
</div>
<h1 class="text-xl font-semibold tracking-tight text-slate-900 dark:text-white">MeshChatX</h1>
diff --git a/meshchatx.rsm b/meshchatx.rsm
index a84ea4e3..910da8dd 100644
Binary files a/meshchatx.rsm and b/meshchatx.rsm differ
diff --git a/meshchatx/meshchat.py b/meshchatx/meshchat.py
index eae52a40..be3a8ba9 100644
--- a/meshchatx/meshchat.py
+++ b/meshchatx/meshchat.py
@@ -152,11 +152,13 @@ from meshchatx.src.backend.map_overlay_sources import OverlaySourceParseError
from meshchatx.src.backend.markdown_renderer import MarkdownRenderer
from meshchatx.src.backend.memory_pressure import MemoryPressureManager, cache_stats
from meshchatx.src.backend.meshchat_utils import (
+ cancel_inbound_deliveries,
convert_db_favourite_to_dict,
convert_propagation_node_state_to_string,
has_attachments,
hex_identifier_to_bytes,
interval_action_due,
+ list_inbound_deliveries,
message_fields_have_attachments,
normalize_hex_identifier,
normalize_identity_storage_hash,
@@ -211,6 +213,10 @@ from meshchatx.src.backend.page_node_manager import PageNodeManager
from meshchatx.src.backend.persistent_log_handler import PersistentLogHandler
from meshchatx.src.backend.plugin_guard import PluginSecurityError
from meshchatx.src.backend.plugin_manager import PluginManager
+from meshchatx.src.backend.active_sessions import (
+ ActiveSessionTracker,
+ should_warn_multi_session,
+)
from meshchatx.src.backend.privacy_mode import (
OutboundHttpBlockedError,
ensure_outbound_http_allowed,
@@ -496,6 +502,7 @@ class ReticulumMeshChat:
self.gitea_base_url_override = gitea_base_url
self._rns_loglevel_cli = rns_loglevel
self.websocket_clients: list[web.WebSocketResponse] = []
+ self.active_sessions = ActiveSessionTracker()
self._websocket_broadcast_lock = asyncio.Lock()
self.listen_host: str | None = None
self.listen_port: int | None = None
@@ -3904,6 +3911,7 @@ class ReticulumMeshChat:
),
),
"inbound_delivery_count": 0,
+ "inbound_deliveries": [],
"max_inbound_syncs": int(
getattr(router, "propagation_max_inbound_syncs", 0) or 0,
),
@@ -3914,9 +3922,9 @@ class ReticulumMeshChat:
getattr(router, "propagation_static_peer_sequential", False),
),
}
- if hasattr(router, "inbound_count"):
- with contextlib.suppress(Exception):
- result["inbound_delivery_count"] = int(router.inbound_count() or 0)
+ inbound_deliveries = list_inbound_deliveries(router)
+ result["inbound_deliveries"] = inbound_deliveries
+ result["inbound_delivery_count"] = len(inbound_deliveries)
return result
def _get_reticulum_section(self):
@@ -7853,9 +7861,15 @@ class ReticulumMeshChat:
# add client to connected clients list
self.websocket_clients.append(websocket_response)
+ session = self.active_sessions.add(
+ ip=request.remote,
+ user_agent=request.headers.get("User-Agent"),
+ )
+ websocket_response._meshchatx_session_id = session["id"]
# send config to all clients
await self.send_config_to_websocket_clients()
+ await self.send_active_sessions_to_websocket_clients()
# handle websocket messages until disconnected
async for msg in websocket_response:
@@ -7877,7 +7891,9 @@ class ReticulumMeshChat:
self.websocket_clients.remove(websocket_response)
except ValueError:
pass
+ self._detach_active_session(websocket_response)
self._cancel_rns_link_tasks_for_client(websocket_response)
+ await self.send_active_sessions_to_websocket_clients()
return websocket_response
@@ -7937,6 +7953,10 @@ class ReticulumMeshChat:
self.web_audio_bridge.detach_client(websocket_response)
return websocket_response
+ @routes.get("/api/v1/app/sessions")
+ async def app_sessions(_request):
+ return web.json_response(self.get_active_sessions_payload())
+
# get app info
@routes.get("/api/v1/app/info")
async def app_info(request):
@@ -12663,9 +12683,10 @@ class ReticulumMeshChat:
if isinstance(transfer_size, (int, float)) and transfer_size > 0:
transfer_size_bytes = int(transfer_size)
inbound_delivery_count = 0
- if router is not None and hasattr(router, "inbound_count"):
- with contextlib.suppress(Exception):
- inbound_delivery_count = int(router.inbound_count() or 0)
+ inbound_deliveries = []
+ if router is not None:
+ inbound_deliveries = list_inbound_deliveries(router)
+ inbound_delivery_count = len(inbound_deliveries)
return web.json_response(
{
"propagation_node_status": {
@@ -12675,6 +12696,7 @@ class ReticulumMeshChat:
"progress": progress_pct,
"transfer_size_bytes": transfer_size_bytes,
"inbound_delivery_count": inbound_delivery_count,
+ "inbound_deliveries": inbound_deliveries,
"messages_received": last_result,
"messages_stored": sync_metrics["messages_stored"],
"delivery_confirmations": sync_metrics[
@@ -12728,26 +12750,41 @@ class ReticulumMeshChat:
@routes.post("/api/v1/lxmf/propagation-node/cancel-inbound")
async def propagation_node_cancel_inbound(request):
router = self.message_router
- if router is None or not hasattr(router, "cancel_all_inbound"):
+ data = {}
+ with contextlib.suppress(Exception):
+ data = await request.json()
+ if not isinstance(data, dict):
+ data = {}
+ resource_hash = data.get("resource_hash")
+ result = cancel_inbound_deliveries(router, resource_hash=resource_hash)
+ if not result.get("ok"):
+ status = 503 if "unavailable" in str(result.get("error") or "") else 400
return web.json_response(
{
- "message": "Inbound delivery cancellation is unavailable.",
+ "message": result.get(
+ "error",
+ "Failed to cancel inbound deliveries",
+ ),
+ "cancelled": result.get("cancelled", 0),
},
- status=503,
+ status=status,
)
- try:
- cancelled = int(router.cancel_all_inbound() or 0)
- except Exception as exc:
- return web.json_response(
- {
- "message": f"Failed to cancel inbound deliveries: {exc}",
- },
- status=500,
+ cancelled = int(result.get("cancelled") or 0)
+ if resource_hash:
+ message = (
+ "Cancelled inbound delivery"
+ if cancelled
+ else "Inbound delivery was not active"
)
+ else:
+ message = f"Cancelled {cancelled} inbound deliveries"
return web.json_response(
{
- "message": f"Cancelled {cancelled} inbound deliveries",
+ "message": message,
"cancelled": cancelled,
+ "resource_hash": result.get("resource_hash"),
+ "inbound_delivery_count": len(list_inbound_deliveries(router)),
+ "inbound_deliveries": list_inbound_deliveries(router),
},
)
@@ -13884,6 +13921,149 @@ class ReticulumMeshChat:
return not_ready
return web.json_response({"files": self.rns_filesync_handler.list_files()})
+ @routes.get("/api/v1/filesync/tree")
+ async def filesync_tree(request):
+ not_ready = _filesync_require_handler()
+ if not_ready is not None:
+ return not_ready
+ path = request.rel_url.query.get("path")
+ try:
+ result = await asyncio.to_thread(
+ self.rns_filesync_handler.list_tree,
+ path,
+ )
+ except Exception as e:
+ return web.json_response({"message": str(e)}, status=500)
+ if not result.get("ok"):
+ return web.json_response(
+ {"message": result.get("error", "list tree failed")},
+ status=400,
+ )
+ return web.json_response(result)
+
+ @routes.post("/api/v1/filesync/mkdir")
+ async def filesync_mkdir(request):
+ not_ready = _filesync_require_handler()
+ if not_ready is not None:
+ return not_ready
+ data = await request.json()
+ if not isinstance(data, dict):
+ return web.json_response({"message": "Invalid JSON body"}, status=400)
+ try:
+ result = await asyncio.to_thread(
+ self.rns_filesync_handler.manager_mkdir,
+ data.get("path", ""),
+ )
+ except Exception as e:
+ return web.json_response({"message": str(e)}, status=500)
+ if not result.get("ok"):
+ return web.json_response(
+ {"message": result.get("error", "mkdir failed")},
+ status=400,
+ )
+ return web.json_response(result)
+
+ @routes.post("/api/v1/filesync/upload")
+ async def filesync_upload(request):
+ not_ready = _filesync_require_handler()
+ if not_ready is not None:
+ return not_ready
+ subdir = None
+ filename = None
+ file_data = None
+ try:
+ reader = await request.multipart()
+ while True:
+ field = await reader.next()
+ if field is None:
+ break
+ name = field.name or ""
+ if name == "path":
+ subdir = (await field.text()).strip() or None
+ elif name == "file":
+ filename = field.filename or "upload"
+ file_data = await field.read()
+ else:
+ with contextlib.suppress(Exception):
+ await field.read()
+ except Exception as e:
+ return web.json_response(
+ {"message": f"Invalid upload request: {e}"},
+ status=400,
+ )
+ if file_data is None:
+ return web.json_response({"message": "No file uploaded"}, status=400)
+ try:
+ result = await asyncio.to_thread(
+ self.rns_filesync_handler.manager_upload,
+ filename=filename,
+ data=file_data,
+ subdir=subdir,
+ )
+ except Exception as e:
+ return web.json_response({"message": str(e)}, status=500)
+ if not result.get("ok"):
+ return web.json_response(
+ {"message": result.get("error", "upload failed")},
+ status=400,
+ )
+ return web.json_response(result)
+
+ @routes.delete("/api/v1/filesync/entry")
+ async def filesync_entry_delete(request):
+ not_ready = _filesync_require_handler()
+ if not_ready is not None:
+ return not_ready
+ data = {}
+ with contextlib.suppress(Exception):
+ data = await request.json()
+ if not isinstance(data, dict):
+ data = {}
+ path = data.get("path", "")
+ try:
+ result = await asyncio.to_thread(
+ self.rns_filesync_handler.manager_delete,
+ path,
+ )
+ except Exception as e:
+ return web.json_response({"message": str(e)}, status=500)
+ if not result.get("ok"):
+ return web.json_response(
+ {"message": result.get("error", "delete failed")},
+ status=400,
+ )
+ return web.json_response(result)
+
+ @routes.get("/api/v1/filesync/content")
+ async def filesync_content(request):
+ not_ready = _filesync_require_handler()
+ if not_ready is not None:
+ return not_ready
+ path = request.rel_url.query.get("path", "")
+ try:
+ result = await asyncio.to_thread(
+ self.rns_filesync_handler.manager_content,
+ path,
+ )
+ except Exception as e:
+ return web.json_response({"message": str(e)}, status=500)
+ if not result.get("ok"):
+ return web.json_response(
+ {"message": result.get("error", "content failed")},
+ status=400,
+ )
+ abspath = result.get("abspath")
+ filename = result.get("filename") or "download"
+ if not abspath or not os.path.isfile(abspath):
+ return web.json_response({"message": "file not found"}, status=404)
+ safe_name = os.path.basename(str(filename)).replace('"', "")
+ return web.FileResponse(
+ abspath,
+ headers={
+ "Content-Disposition": f'attachment; filename="{safe_name}"',
+ },
+ )
+
@routes.get("/api/v1/filesync/directories")
async def filesync_directories(request):
not_ready = _filesync_require_handler()
@@ -19006,6 +19186,11 @@ class ReticulumMeshChat:
self._parse_bool(data["privacy_mode_enabled"]),
)
+ if "multi_session_warning_enabled" in data:
+ self.config.multi_session_warning_enabled.set(
+ self._parse_bool(data["multi_session_warning_enabled"]),
+ )
+
# update map settings
if "map_offline_enabled" in data:
self.config.map_offline_enabled.set(
@@ -19557,6 +19742,8 @@ class ReticulumMeshChat:
# send config to websocket clients
await self.send_config_to_websocket_clients()
+ if "multi_session_warning_enabled" in data:
+ await self.send_active_sessions_to_websocket_clients()
# converts nomadnetwork page variables from a string to a map
# converts: "field1=123|field2=456"
@@ -21040,6 +21227,7 @@ class ReticulumMeshChat:
async def websocket_broadcast(self, data):
# Serialize: concurrent callers must not interleave. The second snapshot must run
# only after the first broadcast has finished mutating the live client list.
+ sessions_changed = False
async with self._websocket_broadcast_lock:
dead = []
# Iterate a copy: awaits allow other tasks to mutate self.websocket_clients.
@@ -21054,10 +21242,46 @@ class ReticulumMeshChat:
self.websocket_clients.remove(client)
except ValueError:
pass
+ if self._detach_active_session(client):
+ sessions_changed = True
try:
await client.close(code=WSCloseCode.GOING_AWAY)
except Exception:
pass
+ if sessions_changed:
+ await self.send_active_sessions_to_websocket_clients()
+
+ def _detach_active_session(self, websocket_response) -> bool:
+ session_id = getattr(websocket_response, "_meshchatx_session_id", None)
+ if not session_id:
+ return False
+ try:
+ delattr(websocket_response, "_meshchatx_session_id")
+ except Exception:
+ pass
+ return self.active_sessions.remove(session_id)
+
+ def get_active_sessions_payload(self) -> dict:
+ snap = self.active_sessions.snapshot()
+ warning_enabled = True
+ try:
+ cfg = getattr(self, "config", None)
+ if cfg is not None and hasattr(cfg, "multi_session_warning_enabled"):
+ warning_enabled = bool(cfg.multi_session_warning_enabled.get())
+ except Exception:
+ warning_enabled = True
+ count = int(snap.get("count") or 0)
+ return {
+ "count": count,
+ "sessions": list(snap.get("sessions") or []),
+ "warning": should_warn_multi_session(count, warning_enabled),
+ "warning_enabled": warning_enabled,
+ }
+
+ async def send_active_sessions_to_websocket_clients(self):
+ payload = self.get_active_sessions_payload()
+ payload["type"] = "app.sessions.updated"
+ await self.websocket_broadcast(json.dumps(payload))
# broadcasts config to all websocket clients
async def send_config_to_websocket_clients(self, context=None):
@@ -21166,6 +21390,7 @@ class ReticulumMeshChat:
"crawler_max_concurrent": ctx.config.crawler_max_concurrent.get(),
"auth_enabled": self.auth_enabled,
"privacy_mode_enabled": ctx.config.privacy_mode_enabled.get(),
+ "multi_session_warning_enabled": ctx.config.multi_session_warning_enabled.get(),
"voicemail_enabled": ctx.config.voicemail_enabled.get(),
"voicemail_greeting": ctx.config.voicemail_greeting.get(),
"voicemail_auto_answer_delay_seconds": ctx.config.voicemail_auto_answer_delay_seconds.get(),
diff --git a/meshchatx/src/backend/active_sessions.py b/meshchatx/src/backend/active_sessions.py
new file mode 100644
index 00000000..0514f26f
--- /dev/null
+++ b/meshchatx/src/backend/active_sessions.py
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: 0BSD
+
+"""Track active UI WebSocket sessions (IP and user-agent) for local multi-client warnings."""
+
+from __future__ import annotations
+
+import threading
+import time
+import uuid
+from typing import Any
+
+
+_MAX_UA_LEN = 512
+_MAX_IP_LEN = 128
+
+
+def _clean_ip(value: str | None) -> str:
+ cleaned = str(value or "").strip()
+ if not cleaned:
+ return "unknown"
+ return cleaned[:_MAX_IP_LEN]
+
+
+def _clean_user_agent(value: str | None) -> str:
+ cleaned = str(value or "").strip()
+ if not cleaned:
+ return "unknown"
+ # Strip control characters that break logs or JSON displays.
+ cleaned = "".join(ch for ch in cleaned if ch.isprintable())
+ if not cleaned:
+ return "unknown"
+ return cleaned[:_MAX_UA_LEN]
+
+
+def should_warn_multi_session(count: int, warning_enabled: bool) -> bool:
+ """Oracle: warn when two or more sessions are active and the setting is on."""
+ try:
+ active = int(count)
+ except (TypeError, ValueError):
+ return False
+ return bool(warning_enabled) and active >= 2
+
+
+class ActiveSessionTracker:
+ """In-memory registry of connected MeshChatX UI WebSocket clients."""
+
+ def __init__(self) -> None:
+ self._lock = threading.RLock()
+ self._sessions: dict[str, dict[str, Any]] = {}
+
+ def add(self, *, ip: str | None, user_agent: str | None) -> dict[str, Any]:
+ session_id = uuid.uuid4().hex
+ entry = {
+ "id": session_id,
+ "ip": _clean_ip(ip),
+ "user_agent": _clean_user_agent(user_agent),
+ "connected_at": time.time(),
+ }
+ with self._lock:
+ self._sessions[session_id] = entry
+ return dict(entry)
+
+ def remove(self, session_id: str | None) -> bool:
+ cleaned = str(session_id or "").strip()
+ if not cleaned:
+ return False
+ with self._lock:
+ return self._sessions.pop(cleaned, None) is not None
+
+ def count(self) -> int:
+ with self._lock:
+ return len(self._sessions)
+
+ def list_sessions(self) -> list[dict[str, Any]]:
+ with self._lock:
+ rows = [dict(row) for row in self._sessions.values()]
+ rows.sort(key=lambda row: float(row.get("connected_at") or 0.0))
+ return rows
+
+ def snapshot(self) -> dict[str, Any]:
+ sessions = self.list_sessions()
+ return {
+ "count": len(sessions),
+ "sessions": sessions,
+ }
diff --git a/meshchatx/src/backend/config_manager.py b/meshchatx/src/backend/config_manager.py
index 19e8119b..ac4810e8 100644
--- a/meshchatx/src/backend/config_manager.py
+++ b/meshchatx/src/backend/config_manager.py
@@ -174,6 +174,11 @@ class ConfigManager:
self.auth_password_hash = self.StringConfig(self, "auth_password_hash", None)
self.auth_session_secret = self.StringConfig(self, "auth_session_secret", None)
self.privacy_mode_enabled = self.BoolConfig(self, "privacy_mode_enabled", False)
+ self.multi_session_warning_enabled = self.BoolConfig(
+ self,
+ "multi_session_warning_enabled",
+ True,
+ )
self.gitea_base_url = self.StringConfig(
self,
"gitea_base_url",
diff --git a/meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt b/meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt
index 2b465b96..1d8193ac 100644
--- a/meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt
+++ b/meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt
@@ -1,5 +1,5 @@
Reticulum MeshChatX - Third-party notices
-Generated at: 2026-07-19T06:42:05.960356Z
+Generated at: 2026-07-22T15:24:54.544053Z
Frontend source: pnpm
Python dependencies
@@ -49,12 +49,9 @@ idna 3.18
lxmf 1.1.0
License: Reticulum License
Author: Mark Qvist
-lxmfy 1.6.5
+lxmfy 2.0.1
License: BSD-0-Clause
Author: Quad4 <team@quad4.io>
-rns-filesync 1.0.0
- License: BSD-2-Clause
- Author: Sudo-Ivan / Quad4.io
lxst 0.5.0
License: Other/Proprietary License
Author: Mark Qvist
@@ -91,6 +88,9 @@ reticulum-meshchatx 4.8.0
rns 1.4.0
License: Reticulum License
Author: Mark Qvist
+rns-filesync 1.0.0
+ License: BSD-2-Clause
+ Author: —
wasmtime 46.0.1
License: Apache-2.0 WITH LLVM-exception
Author: The Wasmtime Project Developers <hello@bytecodealliance.org>
@@ -103,9 +103,6 @@ yarl 1.23.0
Node dependencies
-----------------
-@aashutoshrathi/word-wrap 1.2.6
- License: MIT
- Author: Jon Schlinkert
@asamuzakjp/css-color 5.1.11
License: MIT
Author: asamuzaK
@@ -148,7 +145,7 @@ Node dependencies
@csstools/css-parser-algorithms 4.0.0
License: MIT
Author: —
-@csstools/css-syntax-patches-for-csstree 1.1.6
+@csstools/css-syntax-patches-for-csstree 1.1.3
License: MIT-0
Author: —
@csstools/css-tokenizer 4.0.0
@@ -157,7 +154,7 @@ Node dependencies
@egjs/hammerjs 2.0.17
License: MIT
Author: —
-@electron-internal/extract-zip 1.0.4
+@electron-internal/extract-zip 1.0.1
License: BSD-2-Clause
Author: —
@electron/asar 3.4.1
@@ -175,7 +172,7 @@ Node dependencies
@electron/osx-sign 1.3.3
License: BSD-2-Clause
Author: electron
-@electron/rebuild 4.0.6
+@electron/rebuild 4.0.4
License: MIT
Author: —
@electron/universal 2.0.3
@@ -187,10 +184,10 @@ Node dependencies
@epic-web/invariant 1.0.0
License: MIT
Author: Kent C. Dodds
-@eslint-community/eslint-utils 4.4.0
+@eslint-community/eslint-utils 4.8.0
License: MIT
Author: Toru Nagashima
-@eslint-community/regexpp 4.12.1
+@eslint-community/regexpp 4.12.2
License: MIT
Author: Toru Nagashima
@eslint/config-array 0.21.2
@@ -202,10 +199,10 @@ Node dependencies
@eslint/core 0.17.0
License: Apache-2.0
Author: Nicholas C. Zakas
-@eslint/eslintrc 3.3.5
+@eslint/eslintrc 3.3.6
License: MIT
Author: Nicholas C. Zakas
-@eslint/js 9.39.4
+@eslint/js 9.39.5
License: MIT
Author: —
@eslint/object-schema 2.1.7
@@ -217,39 +214,42 @@ Node dependencies
@exodus/bytes 1.15.0
License: MIT
Author: Exodus Movement, Inc.
-@fontsource/noto-sans 5.2.10
+@fontsource/noto-sans 5.3.0
License: OFL-1.1
Author: Google Inc.
-@humanfs/core 0.19.1
+@gar/promise-retry 1.0.3
+ License: MIT
+ Author: —
+@humanfs/core 0.19.2
+ License: Apache-2.0
+ Author: Nicholas C. Zakas
+@humanfs/node 0.16.8
License: Apache-2.0
Author: Nicholas C. Zakas
-@humanfs/node 0.16.6
+@humanfs/types 0.15.0
License: Apache-2.0
Author: Nicholas C. Zakas
@humanwhocodes/module-importer 1.0.1
License: Apache-2.0
Author: Nicholas C. Zaks
-@humanwhocodes/retry 0.3.0
+@humanwhocodes/retry 0.4.3
License: Apache-2.0
Author: Nicholas C. Zaks
-@intlify/core-base 11.4.6
+@intlify/core-base 11.4.7
License: MIT
Author: kazuya kawaguchi
-@intlify/devtools-types 11.4.6
+@intlify/devtools-types 11.4.7
License: MIT
Author: kazuya kawaguchi
-@intlify/message-compiler 11.4.6
+@intlify/message-compiler 11.4.7
License: MIT
Author: kazuya kawaguchi
-@intlify/shared 11.4.6
+@intlify/shared 11.4.7
License: MIT
Author: kazuya kawaguchi
@isaacs/cliui 8.0.2
License: ISC
Author: Ben Coe
-@isaacs/cliui 9.0.0
- License: BlueOak-1.0.0
- Author: —
@isaacs/fs-minipass 4.0.0
License: ISC
Author: Isaac Z. Schlueter
@@ -274,22 +274,19 @@ Node dependencies
@jridgewell/trace-mapping 0.3.9
License: MIT
Author: Justin Ridgewell
-@keyv/serialize 1.1.1
- License: MIT
- Author: Jared Wray
@malept/cross-spawn-promise 2.0.0
License: Apache-2.0
Author: Mark Lee
@malept/flatpak-bundler 0.4.0
License: MIT
Author: Matt Watson
-@mapbox/jsonlint-lines-primitives 2.0.3
- License: MIT
+@mapbox/jsonlint-lines-primitives 2.0.2
+ License: Unknown
Author: Zach Carter
-@mapbox/unitbezier 1.0.0
+@mapbox/unitbezier 0.0.1
License: BSD-2-Clause
Author: —
-@maplibre/maplibre-gl-style-spec 24.10.0
+@maplibre/maplibre-gl-style-spec 24.4.1
License: ISC
Author: MapLibre
@mdi/font 7.4.47
@@ -301,19 +298,28 @@ Node dependencies
@noble/hashes 1.4.0
License: MIT
Author: Paul Miller
+@npmcli/agent 4.0.2
+ License: ISC
+ Author: GitHub Inc.
+@npmcli/fs 5.0.0
+ License: ISC
+ Author: GitHub Inc.
+@npmcli/redact 4.0.0
+ License: ISC
+ Author: GitHub Inc.
@one-ini/wasm 0.1.1
License: MIT
Author: —
-@oxc-parser/binding-linux-x64-gnu 0.137.0
+@oxc-parser/binding-linux-x64-gnu 0.140.0
License: MIT
Author: Boshen and oxc contributors
-@oxc-project/types 0.133.0
+@oxc-project/types 0.139.0
License: MIT
Author: Boshen and oxc contributors
-@oxc-resolver/binding-linux-x64-gnu 11.21.3
+@oxc-resolver/binding-linux-x64-gnu 11.24.2
License: MIT
Author: —
-@peculiar/asn1-schema 2.8.0
+@peculiar/asn1-schema 2.7.0
License: MIT
Author: PeculiarVentures, LLC
@peculiar/json-schema 1.1.12
@@ -340,49 +346,52 @@ Node dependencies
@polka/url 1.0.0-next.24
License: MIT
Author: Luke Edwards
-@rolldown/binding-linux-x64-gnu 1.0.3
+@rolldown/binding-linux-x64-gnu 1.1.5
License: MIT
Author: —
@rolldown/pluginutils 1.0.0
License: MIT
Author: —
-@sec-ant/readable-stream 0.4.1
- License: MIT
- Author: Ze-Zheng Wu
-@sindresorhus/is 8.1.0
+@sindresorhus/is 4.0.0
License: MIT
Author: Sindre Sorhus
@standard-schema/spec 1.1.0
License: MIT
Author: Colin McDonnell
+@szmarczak/http-timer 4.0.5
+ License: MIT
+ Author: Szymon Marczak
@tailwindcss/forms 0.5.11
License: MIT
Author: —
-@tailwindcss/node 4.2.4
+@tailwindcss/node 4.3.3
License: MIT
Author: —
-@tailwindcss/oxide 4.2.4
+@tailwindcss/oxide 4.3.3
License: MIT
Author: —
-@tailwindcss/oxide-linux-x64-gnu 4.2.4
+@tailwindcss/oxide-linux-x64-gnu 4.3.3
License: MIT
Author: —
-@tailwindcss/vite 4.2.4
+@tailwindcss/vite 4.3.3
License: MIT
Author: —
-@tanstack/virtual-core 3.17.2
+@tanstack/virtual-core 3.17.6
License: MIT
Author: Tanner Linsley
-@tanstack/vue-virtual 3.13.30
+@tanstack/vue-virtual 3.13.34
License: MIT
Author: Tanner Linsley
-@types/chai 5.2.2
+@types/cacheable-request 6.0.1
License: MIT
Author: —
-@types/debug 4.1.13
+@types/chai 5.2.3
License: MIT
Author: —
-@types/deep-eql 4.0.0
+@types/debug 4.1.6
+ License: MIT
+ Author: —
+@types/deep-eql 4.0.2
License: MIT
Author: —
@types/estree 1.0.0
@@ -391,16 +400,16 @@ Node dependencies
@types/fs-extra 9.0.13
License: MIT
Author: —
-@types/hammerjs 2.0.36
+@types/hammerjs 2.0.46
License: MIT
Author: —
-@types/http-cache-semantics 4.2.0
+@types/http-cache-semantics 4.0.0
License: MIT
Author: —
@types/json-schema 7.0.15
License: MIT
Author: —
-@types/ms 2.1.0
+@types/keyv 3.0.0
License: MIT
Author: —
@types/node 0.0.0
@@ -409,37 +418,40 @@ Node dependencies
@types/rbush 4.0.0
License: MIT
Author: —
+@types/responselike 1.0.0
+ License: MIT
+ Author: —
@types/trusted-types 2.0.7
License: MIT
Author: —
-@vitejs/plugin-vue 6.0.7
+@vitejs/plugin-vue 6.0.8
License: MIT
Author: Evan You
-@vitest/coverage-v8 4.1.5
+@vitest/coverage-v8 4.1.10
License: MIT
Author: Anthony Fu
-@vitest/expect 4.1.5
+@vitest/expect 4.1.10
License: MIT
Author: —
-@vitest/mocker 4.1.5
+@vitest/mocker 4.1.10
License: MIT
Author: —
-@vitest/pretty-format 4.1.5
+@vitest/pretty-format 4.1.10
License: MIT
Author: —
-@vitest/runner 4.1.5
+@vitest/runner 4.1.10
License: MIT
Author: —
-@vitest/snapshot 4.1.5
+@vitest/snapshot 4.1.10
License: MIT
Author: —
-@vitest/spy 4.1.5
+@vitest/spy 4.1.10
License: MIT
Author: —
-@vitest/ui 4.1.9
+@vitest/ui 4.1.10
License: MIT
Author: —
-@vitest/utils 4.1.5
+@vitest/utils 4.1.10
License: MIT
Author: —
@volar/language-core 2.4.28
@@ -451,37 +463,37 @@ Node dependencies
@volar/typescript 2.4.28
License: MIT
Author: —
-@vue/compiler-core 3.5.39
+@vue/compiler-core 3.5.40
License: MIT
Author: Evan You
-@vue/compiler-dom 3.5.39
+@vue/compiler-dom 3.5.40
License: MIT
Author: Evan You
-@vue/compiler-sfc 3.5.39
+@vue/compiler-sfc 3.5.40
License: MIT
Author: Evan You
-@vue/compiler-ssr 3.5.39
+@vue/compiler-ssr 3.5.40
License: MIT
Author: Evan You
-@vue/devtools-api 6.6.4
+@vue/devtools-api 6.5.0
License: MIT
Author: Guillaume Chau
-@vue/language-core 3.3.6
+@vue/language-core 3.3.7
License: MIT
Author: —
-@vue/reactivity 3.5.39
+@vue/reactivity 3.5.40
License: MIT
Author: Evan You
-@vue/runtime-core 3.5.39
+@vue/runtime-core 3.5.40
License: MIT
Author: Evan You
-@vue/runtime-dom 3.5.39
+@vue/runtime-dom 3.5.40
License: MIT
Author: Evan You
-@vue/server-renderer 3.5.39
+@vue/server-renderer 3.5.40
License: MIT
Author: Evan You
-@vue/shared 3.5.39
+@vue/shared 3.5.40
License: MIT
Author: Evan You
@vue/test-utils 2.4.11
@@ -493,9 +505,12 @@ Node dependencies
@zarrita/storage 0.2.0
License: MIT
Author: —
-abbrev 2.0.0
+abbrev 4.0.0
License: ISC
Author: GitHub Inc.
+abbrev 1.0.3
+ License: Unknown
+ Author: Isaac Z. Schlueter
acorn 0.11.0
License: MIT
Author: —
@@ -511,6 +526,9 @@ ajv 6.14.0
alien-signals 3.2.1
License: MIT
Author: —
+amdefine 1.0.1
+ License: BSD-3-Clause OR MIT
+ Author: James Burke
ansi-regex 5.0.1
License: MIT
Author: Sindre Sorhus
@@ -523,39 +541,30 @@ app-builder-lib 26.15.3
argparse 2.0.1
License: Python-2.0
Author: —
-array-buffer-byte-length 1.0.2
- License: MIT
- Author: Jordan Harband
-arraybuffer.prototype.slice 1.0.4
- License: MIT
- Author: Jordan Harband
asn1js 3.0.10
License: BSD-3-Clause
Author: Yury Strozhevsky
-ast-types 0.8.15
+assertion-error 2.0.1
+ License: MIT
+ Author: Jake Luer
+ast-types 0.6.12
License: MIT
Author: Ben Newman
ast-v8-to-istanbul 1.0.0
License: MIT
Author: Ari Perkkiö
-async 3.2.6
+async 3.2.3
License: MIT
Author: Caolan McMahon
async-exit-hook 2.0.1
License: MIT
Author: Tapani Moilanen
-async-function 1.0.0
- License: MIT
- Author: Jordan Harbamd
asynckit 0.4.0
License: MIT
Author: Alex Indigo
at-least-node 1.0.0
License: ISC
Author: Ryan Zimmerman
-available-typed-arrays 1.0.7
- License: MIT
- Author: Jordan Harband
aws4 1.13.2
License: MIT
Author: Michael Hart
@@ -577,7 +586,7 @@ blueimp-canvas-to-blob 3.29.0
boolbase 1.0.0
License: ISC
Author: Felix Boehm
-boolean 3.2.0
+boolean 3.0.1
License: MIT
Author: —
brace-expansion 1.1.12
@@ -592,28 +601,22 @@ builder-util 26.15.3
builder-util-runtime 9.7.0
License: MIT
Author: Vladimir Krivosheev
-byte-counter 0.1.0
- License: MIT
- Author: Sindre Sorhus
bytestreamjs 2.0.1
License: BSD-3-Clause
Author: Yury Strozhevsky
-cacheable-lookup 7.0.0
+cacache 20.0.4
+ License: ISC
+ Author: GitHub Inc.
+cacheable-lookup 5.0.3
License: MIT
Author: Szymon Marczak
-cacheable-request 13.0.19
- License: MIT
- Author: Jared Wray
-call-bind 1.0.9
+cacheable-request 7.0.2
License: MIT
- Author: Jordan Harband
-call-bind-apply-helpers 1.0.0
+ Author: Luke Childs
+call-bind-apply-helpers 1.0.1
License: MIT
Author: Jordan Harband
-call-bound 1.0.4
- License: MIT
- Author: Jordan Harband
-callsites 3.0.0
+callsites 3.1.0
License: MIT
Author: Sindre Sorhus
camelcase 5.0.0
@@ -622,7 +625,7 @@ camelcase 5.0.0
chai 6.2.2
License: MIT
Author: Jake Luer
-chalk 4.0.0
+chalk 4.1.1
License: MIT
Author: —
chownr 3.0.0
@@ -631,10 +634,7 @@ chownr 3.0.0
chromium-pickle-js 0.2.0
License: MIT
Author: —
-chunk-data 0.1.0
- License: MIT
- Author: Sindre Sorhus
-ci-info 4.3.1
+ci-info 4.2.0
License: MIT
Author: Thomas Watson Steen
cli-table3 0.5.0
@@ -643,6 +643,9 @@ cli-table3 0.5.0
cliui 4.0.0
License: ISC
Author: Ben Coe
+clone-response 1.0.2
+ License: MIT
+ Author: Luke Childs
code-point-at 1.0.0
License: MIT
Author: Sindre Sorhus
@@ -703,37 +706,28 @@ csstype 3.2.3
data-urls 7.0.0
License: MIT
Author: Domenic Denicola
-data-view-buffer 1.0.2
- License: MIT
- Author: Jordan Harband
-data-view-byte-length 1.0.2
- License: MIT
- Author: Jordan Harband
-data-view-byte-offset 1.0.1
- License: MIT
- Author: Jordan Harband
dayjs 1.11.21
License: MIT
Author: iamkun
debug 4.3.1
License: MIT
- Author: TJ Holowaychuk
+ Author: Josh Junon
decamelize 1.2.0
License: MIT
Author: Sindre Sorhus
decimal.js 10.6.0
License: MIT
Author: Michael Mclaughlin
-decompress-response 10.0.0
+decompress-response 6.0.0
License: MIT
Author: Sindre Sorhus
-deep-is 0.1.3
+deep-is 0.1.4
License: MIT
Author: Thorsten Lorenz
-define-data-property 1.1.4
+defer-to-connect 2.0.0
License: MIT
- Author: Jordan Harband
-define-properties 1.2.1
+ Author: Szymon Marczak
+define-properties 1.1.2
License: MIT
Author: Jordan Harband
delayed-stream 1.0.0
@@ -742,8 +736,8 @@ delayed-stream 1.0.0
detect-libc 2.0.3
License: Apache-2.0
Author: Lovell Fuller
-detect-node 2.1.0
- License: MIT
+detect-node 2.0.4
+ License: ISC
Author: Ilya Kantor
dijkstrajs 1.0.1
License: MIT
@@ -754,13 +748,13 @@ dir-compare 4.2.0
dmg-builder 26.15.3
License: MIT
Author: Vladimir Krivosheev
-dompurify 3.4.11
+dompurify 3.4.12
License: (MPL-2.0 OR Apache-2.0)
Author: Dr.-Ing. Mario Heiderich, Cure53
dotenv 16.6.1
License: BSD-2-Clause
Author: —
-dotenv-expand 11.0.7
+dotenv-expand 11.0.6
License: BSD-2-Clause
Author: motdotla
dunder-proto 1.0.0
@@ -769,13 +763,13 @@ dunder-proto 1.0.0
duplexer2 0.1.4
License: BSD-3-Clause
Author: Conrad Pankoff
-earcut 3.1.0
+earcut 3.0.0
License: ISC
- Author: Volodymyr Agafonkin
+ Author: Vladimir Agafonkin
eastasianwidth 0.2.0
License: MIT
Author: Masaki Komagata
-editorconfig 1.0.7
+editorconfig 1.0.3
License: MIT
Author: EditorConfig Team
ejs 3.1.10
@@ -808,7 +802,10 @@ emoji-picker-element-data 1.8.0
emoji-regex 8.0.0
License: MIT
Author: Mathias Bynens
-enhanced-resolve 5.19.0
+end-of-stream 1.1.0
+ License: MIT
+ Author: Mathias Buus
+enhanced-resolve 5.24.1
License: MIT
Author: Tobias Koppers @sokra
entities 7.0.1
@@ -817,13 +814,10 @@ entities 7.0.1
env-paths 2.2.0
License: MIT
Author: Sindre Sorhus
-err-code 2.0.3
+err-code 2.0.2
License: MIT
Author: IndigoUnited
-es-abstract 1.24.2
- License: MIT
- Author: Jordan Harband
-es-abstract-get 1.0.0
+es-abstract 1.5.0
License: MIT
Author: Jordan Harband
es-define-property 1.0.1
@@ -832,7 +826,7 @@ es-define-property 1.0.1
es-errors 1.3.0
License: MIT
Author: Jordan Harband
-es-module-lexer 2.2.0
+es-module-lexer 2.0.0
License: MIT
Author: Guy Bedford
es-object-atoms 1.0.0
@@ -841,7 +835,7 @@ es-object-atoms 1.0.0
es-set-tostringtag 2.1.0
License: MIT
Author: Jordan Harband
-es-to-primitive 1.3.4
+es-to-primitive 1.1.0
License: MIT
Author: Jordan Harband
es6-error 4.1.1
@@ -853,7 +847,7 @@ escalade 3.1.1
escape-string-regexp 4.0.0
License: MIT
Author: Sindre Sorhus
-eslint 9.39.4
+eslint 9.39.5
License: MIT
Author: Nicholas C. Zakas
eslint-config-prettier 10.1.8
@@ -865,13 +859,13 @@ eslint-plugin-prettier 5.5.6
eslint-plugin-security 3.0.1
License: Apache-2.0
Author: Node Security Project
-eslint-plugin-vue 10.9.2
+eslint-plugin-vue 10.10.0
License: MIT
Author: Toru Nagashima
eslint-scope 8.2.0
License: BSD-2-Clause
Author: —
-eslint-visitor-keys 3.3.0
+eslint-visitor-keys 3.4.3
License: Apache-2.0
Author: Toru Nagashima
esmangle-evaluator 1.0.0
@@ -880,10 +874,10 @@ esmangle-evaluator 1.0.0
espree 10.3.0
License: BSD-2-Clause
Author: Nicholas C. Zakas
-esprima-fb 15001.1001.0-dev-harmony-fb
+esprima-fb 12001.1.0-dev-harmony-fb
License: BSD
Author: Ariya Hidayat
-esquery 1.5.0
+esquery 1.6.0
License: BSD-3-Clause
Author: Joel Feenstra
esrecurse 4.3.0
@@ -895,8 +889,8 @@ estraverse 5.1.0
estree-walker 2.0.2
License: MIT
Author: Rich Harris
-esutils 2.0.2
- License: BSD
+esutils 2.0.3
+ License: BSD-2-Clause
Author: —
execa 0.10.0
License: MIT
@@ -916,16 +910,16 @@ falafel 1.0.1
fast-deep-equal 3.1.3
License: MIT
Author: Evgeny Poberezkin
-fast-diff 1.3.0
+fast-diff 1.1.2
License: Apache-2.0
Author: Jason Chen
-fast-json-stable-stringify 2.0.0
+fast-json-stable-stringify 2.1.0
License: MIT
Author: James Halliday
fast-levenshtein 2.0.6
License: MIT
Author: Ramesh Nair
-fast-uri 3.1.2
+fast-uri 4.1.1
License: BSD-3-Clause
Author: Vincent Le Goff
fd-package-json 2.0.0
@@ -946,15 +940,18 @@ filelist 1.0.1
find-up 3.0.0
License: MIT
Author: Sindre Sorhus
-flat-cache 4.0.0
+flat-cache 4.0.1
License: MIT
Author: Jared Wray
flatted 3.4.2
License: ISC
Author: Andrea Giammarchi
-for-each 0.3.5
+for-each 0.3.3
License: MIT
Author: Raynos
+foreach 2.0.6
+ License: MIT
+ Author: Manuel Stofer
foreground-child 3.1.0
License: ISC
Author: Isaac Z. Schlueter
@@ -967,39 +964,27 @@ formatly 0.3.0
fs-extra 7.0.1
License: MIT
Author: JP Richardson
+fs-minipass 3.0.3
+ License: ISC
+ Author: GitHub Inc.
fs.realpath 1.0.0
License: ISC
Author: Isaac Z. Schlueter
-function-bind 1.1.1
+function-bind 1.0.2
License: MIT
Author: Raynos
-function.prototype.name 1.2.0
- License: MIT
- Author: Jordan Harband
-functions-have-names 1.2.3
- License: MIT
- Author: Jordan Harband
-generator-function 2.0.0
- License: MIT
- Author: Jordan Harbamd
geotiff 3.0.5
License: MIT
Author: Fabian Schindler
get-caller-file 1.0.1
License: ISC
Author: Stefan Penner
-get-intrinsic 1.1.3
- License: MIT
- Author: Jordan Harband
-get-proto 1.0.1
+get-intrinsic 1.2.6
License: MIT
Author: Jordan Harband
get-stream 3.0.0
License: MIT
Author: Sindre Sorhus
-get-symbol-description 1.1.0
- License: MIT
- Author: Jordan Harband
get-tsconfig 4.14.0
License: MIT
Author: Hiroki Osame
@@ -1015,45 +1000,33 @@ global-agent 3.0.0
globals 14.0.0
License: MIT
Author: Sindre Sorhus
-globalthis 1.0.4
+globalthis 1.0.1
License: MIT
Author: Jordan Harband
-gopd 1.0.1
+gopd 1.2.0
License: MIT
Author: Jordan Harband
-got 15.0.7
+got 11.8.6
License: MIT
Author: —
graceful-fs 4.1.2
License: ISC
Author: —
-has 1.0.3
- License: MIT
- Author: Thiago de Arruda
-has-bigints 1.1.0
- License: MIT
- Author: Jordan Harband
has-flag 4.0.0
License: MIT
Author: Sindre Sorhus
-has-property-descriptors 1.0.2
- License: MIT
- Author: Jordan Harband
-has-proto 1.2.0
- License: MIT
- Author: Jordan Harband
has-symbols 1.0.3
License: MIT
Author: Jordan Harband
has-tostringtag 1.0.2
License: MIT
Author: Jordan Harband
-hasown 2.0.2
+hasown 2.0.4
License: MIT
Author: Jordan Harband
-hosted-git-info 10.1.1
+hosted-git-info 2.8.9
License: ISC
- Author: GitHub Inc.
+ Author: Rebecca Turner
html-encoding-sniffer 6.0.0
License: MIT
Author: Domenic Denicola
@@ -1066,19 +1039,22 @@ http-cache-semantics 4.1.1
http-proxy-agent 7.0.0
License: MIT
Author: Nathan Rajlich
-http2-wrapper 2.2.1
+http2-wrapper 1.0.0-beta.5.2
License: MIT
Author: Szymon Marczak
https-proxy-agent 7.0.0
License: MIT
Author: Nathan Rajlich
-ignore 5.2.0
+iconv-lite 0.7.2
+ License: MIT
+ Author: Alexander Shtuchkin
+ignore 5.3.2
License: MIT
Author: kael
immediate 3.0.5
License: MIT
Author: —
-import-fresh 3.2.1
+import-fresh 3.3.1
License: MIT
Author: Sindre Sorhus
imurmurhash 0.1.4
@@ -1099,94 +1075,40 @@ ini 1.3.6
inline-process-browser 1.0.0
License: MIT
Author: Calvin W. Metcalf
-internal-slot 1.1.0
- License: MIT
- Author: Jordan Harband
invert-kv 2.0.0
License: MIT
Author: Sindre Sorhus
-is-array-buffer 3.0.5
- License: MIT
- Author: Jordan Harband
-is-async-function 2.1.1
+ip-address 9.0.5
License: MIT
- Author: Jordan Harband
-is-bigint 1.1.0
- License: MIT
- Author: Jordan Harband
+ Author: Beau Gunderson
is-blob 2.1.0
License: MIT
Author: Sindre Sorhus
-is-boolean-object 1.2.0
- License: MIT
- Author: Jordan Harband
-is-callable 1.2.7
- License: MIT
- Author: Jordan Harband
-is-data-view 1.0.2
+is-callable 1.1.1
License: MIT
Author: Jordan Harband
-is-date-object 1.1.0
- License: MIT
- Author: Jordan Harband
-is-document.all 1.0.0
+is-date-object 1.0.1
License: MIT
Author: Jordan Harband
is-extglob 2.1.1
License: MIT
Author: Jon Schlinkert
-is-finalizationregistry 1.1.1
- License: MIT
- Author: Jordan Harband
is-fullwidth-code-point 1.0.0
License: MIT
Author: Sindre Sorhus
-is-generator-function 1.1.2
- License: MIT
- Author: Jordan Harband
-is-glob 4.0.0
+is-glob 4.0.3
License: MIT
Author: Jon Schlinkert
-is-map 2.0.3
- License: MIT
- Author: Jordan Harband
-is-negative-zero 2.0.3
- License: MIT
- Author: Jordan Harband
-is-number-object 1.1.0
- License: MIT
- Author: Jordan Harband
is-potential-custom-element-name 1.0.1
License: MIT
Author: Mathias Bynens
-is-regex 1.2.1
- License: MIT
- Author: Jordan Harband
-is-set 2.0.3
- License: MIT
- Author: Jordan Harband
-is-shared-array-buffer 1.0.4
+is-regex 1.0.3
License: MIT
Author: Jordan Harband
is-stream 1.1.0
License: MIT
Author: Sindre Sorhus
-is-string 1.1.1
- License: MIT
- Author: Jordan Harband
-is-symbol 1.1.0
- License: MIT
- Author: Jordan Harband
-is-typed-array 1.1.15
- License: MIT
- Author: Jordan Harband
-is-weakmap 2.0.2
- License: MIT
- Author: Jordan Harband
-is-weakref 1.1.1
- License: MIT
- Author: Jordan Harband
-is-weakset 2.0.4
+is-symbol 1.0.1
License: MIT
Author: Jordan Harband
isarray 0.0.1
@@ -1198,6 +1120,9 @@ isbinaryfile 4.0.8
isexe 2.0.0
License: ISC
Author: Isaac Z. Schlueter
+isexe 3.1.5
+ License: BlueOak-1.0.0
+ Author: Isaac Z. Schlueter
istanbul-lib-coverage 3.2.2
License: BSD-3-Clause
Author: Krishnan Anantheswaran
@@ -1216,18 +1141,18 @@ jake 10.8.5
jiti 2.4.2
License: MIT
Author: —
-js-beautify 1.15.4
+js-beautify 1.14.9
License: MIT
Author: Einar Lielmanis
-js-cookie 3.0.8
- License: MIT
- Author: Klaus Hartl
js-tokens 10.0.0
License: MIT
Author: Simon Lydell
js-yaml 4.2.0
License: MIT
Author: Vladimir Zapparov
+jsbn 1.1.0
+ License: MIT
+ Author: Tom Wu
jsdom 29.1.1
License: MIT
Author: —
@@ -1261,10 +1186,10 @@ jszip 3.10.1
keycharm 0.4.0
License: (Apache-2.0 OR MIT)
Author: Alex de Mulder
-keyv 4.5.4
+keyv 4.0.0
License: MIT
Author: Jared Wray
-knip 6.24.0
+knip 6.29.0
License: ISC
Author: Lars Kappert
lazy-val 1.0.5
@@ -1297,7 +1222,7 @@ lodash 4.18.0
lodash.merge 4.6.2
License: MIT
Author: John-David Dalton
-lowercase-keys 3.0.0
+lowercase-keys 2.0.0
License: MIT
Author: Sindre Sorhus
lru-cache 6.0.0
@@ -1315,10 +1240,13 @@ magicast 0.5.2
make-dir 4.0.0
License: MIT
Author: Sindre Sorhus
+make-fetch-happen 15.0.6
+ License: ISC
+ Author: GitHub Inc.
mapbox-to-css-font 3.2.0
License: BSD-2-Clause
Author: Andreas Hocevar
-marked 18.0.5
+marked 18.0.7
License: MIT
Author: Christopher Jeffrey
matcher 3.0.0
@@ -1336,7 +1264,7 @@ mem 3.0.1
micron-parser 0.0.0
License: Unknown
Author: —
-mime 2.6.0
+mime 2.5.2
License: MIT
Author: Robert Kieffer
mime-db 1.52.0
@@ -1345,10 +1273,10 @@ mime-db 1.52.0
mime-types 2.1.35
License: MIT
Author: —
-mimic-fn 1.2.0
+mimic-fn 1.0.0
License: MIT
Author: Sindre Sorhus
-mimic-response 4.0.0
+mimic-response 1.0.0
License: MIT
Author: Sindre Sorhus
mini-svg-data-uri 1.2.3
@@ -1363,15 +1291,27 @@ minimatch 10.2.3
minimist 1.2.8
License: MIT
Author: James Halliday
-minipass 7.1.2
+minipass 3.0.0
+ License: ISC
+ Author: Isaac Z. Schlueter
+minipass-collect 2.0.1
License: ISC
Author: Isaac Z. Schlueter
-minizlib 3.1.0
+minipass-fetch 5.0.2
License: MIT
+ Author: GitHub Inc.
+minipass-flush 1.0.7
+ License: BlueOak-1.0.0
+ Author: Isaac Z. Schlueter
+minipass-pipeline 1.2.4
+ License: ISC
+ Author: Isaac Z. Schlueter
+minipass-sized 2.0.0
+ License: ISC
Author: Isaac Z. Schlueter
-mkdirp 0.5.1
+minizlib 3.0.1
License: MIT
- Author: James Halliday
+ Author: Isaac Z. Schlueter
mrmime 2.0.0
License: MIT
Author: Luke Edwards
@@ -1387,25 +1327,28 @@ nanoid 3.3.12
natural-compare 1.4.0
License: MIT
Author: Lauri Rooden
-node-abi 4.31.0
+negotiator 1.0.0
+ License: MIT
+ Author: —
+node-abi 4.2.0
License: MIT
Author: Lukas Geiger
node-api-version 0.2.1
License: MIT
Author: Tim Fish
-node-gyp 12.4.0
+node-gyp 12.2.0
License: MIT
Author: Nathan Rajlich
node-int64 0.4.0
License: MIT
Author: Robert Kieffer
-nopt 7.2.1
+nopt 6.0.0
License: ISC
Author: GitHub Inc.
-normalize-url 8.1.1
+normalize-url 6.0.1
License: MIT
Author: Sindre Sorhus
-npm-run-path 2.0.2
+npm-run-path 2.0.0
License: MIT
Author: Sindre Sorhus
nth-check 2.1.1
@@ -1420,13 +1363,7 @@ numcodecs 0.3.2
object-assign 4.1.0
License: MIT
Author: Sindre Sorhus
-object-inspect 1.13.4
- License: MIT
- Author: James Halliday
-object-keys 1.1.1
- License: MIT
- Author: Jordan Harband
-object.assign 4.1.7
+object-keys 1.0.8
License: MIT
Author: Jordan Harband
obug 2.1.1
@@ -1438,24 +1375,24 @@ ol 10.9.0
ol-mapbox-style 13.4.1
License: BSD-2-Clause
Author: —
-once 1.4.0
- License: ISC
+once 1.3.0
+ License: BSD
Author: Isaac Z. Schlueter
-optionator 0.9.3
+optionator 0.9.4
License: MIT
Author: George Zahariev
os-locale 3.0.0
License: MIT
Author: Sindre Sorhus
-own-keys 1.0.1
- License: MIT
- Author: Jordan Harband
-oxc-parser 0.137.0
+oxc-parser 0.140.0
License: MIT
Author: Boshen and oxc contributors
-oxc-resolver 11.21.3
+oxc-resolver 11.24.2
License: MIT
Author: —
+p-cancelable 2.0.0
+ License: MIT
+ Author: Sindre Sorhus
p-finally 1.0.0
License: MIT
Author: Sindre Sorhus
@@ -1468,6 +1405,9 @@ p-limit 2.0.0
p-locate 3.0.0
License: MIT
Author: Sindre Sorhus
+p-map 7.0.6
+ License: MIT
+ Author: Sindre Sorhus
p-try 2.0.0
License: MIT
Author: Sindre Sorhus
@@ -1480,7 +1420,7 @@ pako 1.0.2
pako 2.0.4
License: (MIT AND Zlib)
Author: —
-parent-module 1.0.0
+parent-module 1.0.1
License: MIT
Author: Sindre Sorhus
parse-headers 2.0.2
@@ -1510,7 +1450,7 @@ pathe 2.0.3
pbf 4.0.1
License: BSD-3-Clause
Author: Konstantin Kaefer
-pe-library 0.4.1
+pe-library 0.4.0
License: MIT
Author: jet
picocolors 1.1.1
@@ -1534,13 +1474,10 @@ plist 3.0.5
pngjs 5.0.0
License: MIT
Author: —
-possible-typed-array-names 1.1.0
- License: MIT
- Author: Jordan Harband
-postcss 8.5.15
+postcss 8.5.19
License: MIT
Author: Andrey Sitnik
-postcss-selector-parser 7.1.0
+postcss-selector-parser 7.1.4
License: MIT
Author: —
postject 1.0.0-alpha.6
@@ -1549,7 +1486,7 @@ postject 1.0.0-alpha.6
prelude-ls 1.2.1
License: MIT
Author: George Zahariev
-prettier 3.9.3
+prettier 3.9.6
License: MIT
Author: James Long
prettier-linter-helpers 1.0.1
@@ -1558,7 +1495,7 @@ prettier-linter-helpers 1.0.1
private 0.1.5
License: MIT
Author: Ben Newman
-proc-log 6.1.0
+proc-log 6.0.0
License: ISC
Author: GitHub Inc.
process-nextick-args 1.0.0
@@ -1573,19 +1510,22 @@ promise-retry 2.0.1
proper-lockfile 4.1.2
License: MIT
Author: André Cruz
-proto-list 1.2.4
- License: ISC
+proto-list 1.2.1
+ License: MIT
Author: Isaac Z. Schlueter
protocol-buffers-schema 3.6.1
License: MIT
Author: Mathias Buus
-punycode 2.1.0
+pump 3.0.0
+ License: MIT
+ Author: Mathias Buus Madsen
+punycode 2.3.1
License: MIT
Author: Mathias Bynens
pvtsutils 1.3.6
License: MIT
Author: PeculiarVentures
-pvutils 1.1.5
+pvutils 1.1.3
License: MIT
Author: Yury Strozhevsky
qrcode 1.5.4
@@ -1606,21 +1546,15 @@ read-binary-file-arch 1.0.6
readable-stream 1.0.31
License: MIT
Author: —
-recast 0.10.43
+recast 0.10.1
License: MIT
Author: Ben Newman
reference-spec-reader 0.2.0
License: MIT
Author: manzt
-reflect.getprototypeof 1.0.10
- License: MIT
- Author: Jordan Harband
regexp-tree 0.1.1
License: MIT
Author: Dmitry Soshnikov
-regexp.prototype.flags 1.5.4
- License: MIT
- Author: Jordan Harband
require-directory 2.1.1
License: MIT
Author: Troy Goode
@@ -1630,10 +1564,10 @@ require-from-string 2.0.2
require-main-filename 1.0.1
License: ISC
Author: Ben Coe
-resedit 1.7.2
+resedit 1.7.0
License: MIT
Author: jet
-resolve-alpn 1.2.1
+resolve-alpn 1.0.0
License: MIT
Author: Szymon Marczak
resolve-from 4.0.0
@@ -1645,37 +1579,34 @@ resolve-pkg-maps 1.0.0
resolve-protobuf-schema 2.1.0
License: MIT
Author: Mathias Buus
-responselike 4.0.2
+responselike 2.0.0
License: MIT
- Author: —
+ Author: lukechilds
retry 0.12.0
License: MIT
Author: Tim Koschützki
rimraf 2.6.2
License: ISC
Author: Isaac Z. Schlueter
-roarr 2.15.4
+roarr 2.15.3
License: BSD-3-Clause
Author: Gajus Kuizinas
-rolldown 1.0.3
+rolldown 1.1.5
License: MIT
Author: —
-safe-array-concat 1.1.4
- License: MIT
- Author: Jordan Harband
+rw 1.3.3
+ License: BSD-3-Clause
+ Author: Mike Bostock
safe-buffer 5.1.1
License: MIT
Author: Feross Aboukhadijeh
-safe-push-apply 1.0.0
- License: MIT
- Author: Jordan Harband
safe-regex 2.1.1
License: MIT
Author: James C.
-safe-regex-test 1.1.0
+safer-buffer 2.1.2
License: MIT
- Author: Jordan Harband
-sanitize-filename 1.6.4
+ Author: Nikita Skovoroda
+sanitize-filename 1.6.3
License: WTFPL OR ISC
Author: Parsha Pourkhomami
sax 1.2.4
@@ -1696,15 +1627,6 @@ serialize-error 7.0.1
set-blocking 2.0.0
License: ISC
Author: Ben Coe
-set-function-length 1.2.2
- License: MIT
- Author: Jordan Harband
-set-function-name 2.0.2
- License: MIT
- Author: Jordan Harband
-set-proto 1.0.0
- License: MIT
- Author: Jordan Harband
setimmediate 1.0.5
License: MIT
Author: YuzuJS
@@ -1714,18 +1636,6 @@ shebang-command 2.0.0
shebang-regex 3.0.0
License: MIT
Author: Sindre Sorhus
-side-channel 1.1.1
- License: MIT
- Author: Jordan Harband
-side-channel-list 1.0.1
- License: MIT
- Author: Jordan Harband
-side-channel-map 1.0.1
- License: MIT
- Author: Jordan Harband
-side-channel-weakmap 1.0.2
- License: MIT
- Author: Jordan Harband
siginfo 2.0.0
License: ISC
Author: Emil Bay
@@ -1738,21 +1648,36 @@ simple-update-notifier 2.0.0
sirv 3.0.2
License: MIT
Author: Luke Edwards
+smart-buffer 4.2.0
+ License: MIT
+ Author: Josh Glazebrook
smol-toml 1.7.0
License: BSD-3-Clause
Author: Cynthia Rey
-source-map 0.5.0
+socks 2.8.0
+ License: MIT
+ Author: Josh Glazebrook
+socks-proxy-agent 8.0.3
+ License: MIT
+ Author: Nathan Rajlich
+source-map 0.6.0
License: BSD-3-Clause
Author: Nick Fitzgerald
+source-map 0.3.0
+ License: BSD
+ Author: Nick Fitzgerald
source-map-js 1.0.2
License: BSD-3-Clause
Author: Valentin 7rulnik Semirulnik
source-map-support 0.5.19
License: MIT
Author: —
-sprintf-js 1.1.3
+sprintf-js 1.1.2
License: BSD-3-Clause
Author: Alexandru Mărășteanu
+ssri 13.0.1
+ License: ISC
+ Author: GitHub Inc.
stackback 0.0.2
License: MIT
Author: Roman Shtylman
@@ -1762,19 +1687,10 @@ stat-mode 1.0.0
std-env 4.0.0-rc.1
License: MIT
Author: —
-stop-iteration-iterator 1.1.0
- License: MIT
- Author: Jordan Harband
string-width 1.0.1
License: MIT
Author: Sindre Sorhus
-string.prototype.trim 1.2.11
- License: MIT
- Author: Jordan Harband
-string.prototype.trimend 1.0.10
- License: MIT
- Author: Jordan Harband
-string.prototype.trimstart 1.0.8
+string.prototype.trim 1.1.2
License: MIT
Author: Jordan Harband
string_decoder 0.10.24
@@ -1801,25 +1717,22 @@ symbol-tree 3.2.4
synckit 0.11.13
License: MIT
Author: JounQin
-tagged-tag 1.0.0
- License: MIT
- Author: Sindre Sorhus
-tailwindcss 4.2.4
+tailwindcss 4.3.3
License: MIT
Author: —
-tapable 2.3.0
+tapable 2.3.3
License: MIT
Author: Tobias Koppers @sokra
-tar 7.5.19
+tar 7.5.21
License: BlueOak-1.0.0
Author: Isaac Z. Schlueter
-temp 0.9.4
+temp 0.9.0
License: MIT
Author: Bruce Williams
temp-file 3.4.0
License: MIT
Author: Vladimir Krivosheev
-terser 5.48.0
+terser 5.49.0
License: BSD-2-Clause
Author: Mihai Bazon
through2 0.6.2
@@ -1843,16 +1756,16 @@ tinyqueue 3.0.0
tinyrainbow 3.1.0
License: MIT
Author: —
-tldts 7.4.5
+tldts 7.0.5
License: MIT
Author: Rémi Berson
-tldts-core 7.4.5
+tldts-core 7.0.5
License: MIT
Author: Rémi Berson
tmp 0.2.7
License: MIT
Author: KARASZI István
-tmp-promise 3.0.3
+tmp-promise 3.0.2
License: MIT
Author: Benjamin Gruenbaum and Collaborators.
totalist 3.0.0
@@ -1864,10 +1777,10 @@ tough-cookie 6.0.1
tr46 6.0.0
License: MIT
Author: Sebastian Mayr
-truncate-utf8-bytes 1.0.2
+truncate-utf8-bytes 1.0.0
License: WTFPL
Author: Carl Xiong
-tslib 2.4.0
+tslib 2.8.1
License: 0BSD
Author: Microsoft Corp.
type-check 0.4.0
@@ -1876,30 +1789,12 @@ type-check 0.4.0
type-fest 0.13.1
License: (MIT OR CC0-1.0)
Author: Sindre Sorhus
-typed-array-buffer 1.0.3
- License: MIT
- Author: Jordan Harband
-typed-array-byte-length 1.0.3
- License: MIT
- Author: Jordan Harband
-typed-array-byte-offset 1.0.4
- License: MIT
- Author: Jordan Harband
-typed-array-length 1.0.8
- License: MIT
- Author: Jordan Harband
typescript 6.0.3
License: Apache-2.0
Author: Microsoft Corp.
-uint8array-extras 1.5.0
- License: MIT
- Author: Sindre Sorhus
-unbash 4.0.2
+unbash 4.0.3
License: ISC
Author: Lars Kappert
-unbox-primitive 1.1.0
- License: MIT
- Author: Jordan Harband
undici 7.28.0
License: MIT
Author: —
@@ -1915,17 +1810,17 @@ unreachable-branch-transform 0.3.0
unzipit 2.0.0
License: MIT
Author: —
-unzipper 0.12.5
+unzipper 0.12.3
License: MIT
- Author: Ziggy Jonsson
+ Author: Evan Oxfeld
upath 2.0.1
License: MIT
Author: Angelos Pikoulas
-uri-js 4.2.2
+uri-js 4.4.1
License: BSD-2-Clause
Author: Gary Court
-utf8-byte-length 1.0.5
- License: (WTFPL OR MIT)
+utf8-byte-length 1.0.1
+ License: WTFPL
Author: Carl Xiong
util-deprecate 1.0.1
License: MIT
@@ -1942,37 +1837,37 @@ vis-network 9.1.13
vis-util 5.0.7
License: (Apache-2.0 OR MIT)
Author: Alex de Mulder
-vite 8.0.16
+vite 8.1.5
License: MIT
Author: Evan You
vite-plugin-vuetify 2.1.3
License: MIT
Author: Kael Watts-Deuchar
-vitest 4.1.5
+vitest 4.1.10
License: MIT
Author: Anthony Fu
-vscode-uri 3.1.0
+vscode-uri 3.0.8
License: MIT
Author: Microsoft
-vue 3.5.39
+vue 3.5.40
License: MIT
Author: Evan You
-vue-component-type-helpers 3.3.5
+vue-component-type-helpers 3.0.0
License: MIT
Author: —
vue-eslint-parser 10.4.1
License: MIT
Author: Toru Nagashima
-vue-i18n 11.4.6
+vue-i18n 11.4.7
License: MIT
Author: kazuya kawaguchi
vue-router 4.6.4
License: MIT
Author: Eduardo San Martin Morote
-vue-tsc 3.3.6
+vue-tsc 3.3.7
License: MIT
Author: —
-vuetify 3.12.8
+vuetify 3.12.10
License: MIT
Author: John Leider
w3c-xmlserializer 5.0.0
@@ -1999,31 +1894,22 @@ whatwg-url 16.0.1
which 2.0.1
License: ISC
Author: GitHub Inc.
-which-boxed-primitive 1.1.0
- License: MIT
- Author: Jordan Harband
-which-builtin-type 1.2.1
- License: MIT
- Author: Jordan Harband
-which-collection 1.0.2
- License: MIT
- Author: Jordan Harband
which-module 2.0.0
License: ISC
Author: nexdrew
-which-typed-array 1.1.22
- License: MIT
- Author: Jordan Harband
why-is-node-running 2.3.0
License: MIT
Author: Mathias Buus
+word-wrap 1.2.5
+ License: MIT
+ Author: Jon Schlinkert
wrap-ansi 2.0.0
License: MIT
Author: Sindre Sorhus
-wrappy 1.0.2
+wrappy 1.0.0
License: ISC
Author: Isaac Z. Schlueter
-xml-name-validator 4.0.0
+xml-name-validator 5.0.0
License: Apache-2.0
Author: Domenic Denicola
xml-utils 1.10.2
diff --git a/meshchatx/src/backend/data/licenses_backend.json b/meshchatx/src/backend/data/licenses_backend.json
index ef16fc96..f372a171 100644
--- a/meshchatx/src/backend/data/licenses_backend.json
+++ b/meshchatx/src/backend/data/licenses_backend.json
@@ -8,7 +8,7 @@
{
"name": "aiohttp",
"version": "3.14.1",
- "author": "\u2014",
+ "author": "—",
"license": "Apache-2.0 AND MIT"
},
{
@@ -32,7 +32,7 @@
{
"name": "audioop-lts",
"version": "0.2.2",
- "author": "\u2014",
+ "author": "—",
"license": "PSF-2.0"
},
{
@@ -50,7 +50,7 @@
{
"name": "cbor2",
"version": "6.1.1",
- "author": "Alex Gr\u00f6nholm <alex.gronholm@nextday.fi>",
+ "author": "Alex Grönholm <alex.gronholm@nextday.fi>",
"license": "MIT"
},
{
@@ -91,16 +91,10 @@
},
{
"name": "lxmfy",
- "version": "1.6.5",
+ "version": "2.0.1",
"author": "Quad4 <team@quad4.io>",
"license": "BSD-0-Clause"
},
- {
- "name": "rns-filesync",
- "version": "1.0.0",
- "author": "Sudo-Ivan / Quad4.io",
- "license": "BSD-2-Clause"
- },
{
"name": "lxst",
"version": "0.5.0",
@@ -173,6 +167,12 @@
"author": "Mark Qvist",
"license": "Reticulum License"
},
+ {
+ "name": "rns-filesync",
+ "version": "1.0.0",
+ "author": "—",
+ "license": "BSD-2-Clause"
+ },
{
"name": "wasmtime",
"version": "46.0.1",
diff --git a/meshchatx/src/backend/data/licenses_frontend.json b/meshchatx/src/backend/data/licenses_frontend.json
index 98c0be7f..e646aea6 100644
--- a/meshchatx/src/backend/data/licenses_frontend.json
+++ b/meshchatx/src/backend/data/licenses_frontend.json
@@ -1,10 +1,4 @@
[
- {
- "name": "@aashutoshrathi/word-wrap",
- "version": "1.2.6",
- "author": "Jon Schlinkert",
- "license": "MIT"
- },
{
"name": "@asamuzakjp/css-color",
"version": "5.1.11",
@@ -91,7 +85,7 @@
},
{
"name": "@csstools/css-syntax-patches-for-csstree",
- "version": "1.1.6",
+ "version": "1.1.3",
"author": "—",
"license": "MIT-0"
},
@@ -109,7 +103,7 @@
},
{
"name": "@electron-internal/extract-zip",
- "version": "1.0.4",
+ "version": "1.0.1",
"author": "—",
"license": "BSD-2-Clause"
},
@@ -145,7 +139,7 @@
},
{
"name": "@electron/rebuild",
- "version": "4.0.6",
+ "version": "4.0.4",
"author": "—",
"license": "MIT"
},
@@ -169,13 +163,13 @@
},
{
"name": "@eslint-community/eslint-utils",
- "version": "4.4.0",
+ "version": "4.8.0",
"author": "Toru Nagashima",
"license": "MIT"
},
{
"name": "@eslint-community/regexpp",
- "version": "4.12.1",
+ "version": "4.12.2",
"author": "Toru Nagashima",
"license": "MIT"
},
@@ -199,13 +193,13 @@
},
{
"name": "@eslint/eslintrc",
- "version": "3.3.5",
+ "version": "3.3.6",
"author": "Nicholas C. Zakas",
"license": "MIT"
},
{
"name": "@eslint/js",
- "version": "9.39.4",
+ "version": "9.39.5",
"author": "—",
"license": "MIT"
},
@@ -229,19 +223,31 @@
},
{
"name": "@fontsource/noto-sans",
- "version": "5.2.10",
+ "version": "5.3.0",
"author": "Google Inc.",
"license": "OFL-1.1"
},
+ {
+ "name": "@gar/promise-retry",
+ "version": "1.0.3",
+ "author": "—",
+ "license": "MIT"
+ },
{
"name": "@humanfs/core",
- "version": "0.19.1",
+ "version": "0.19.2",
"author": "Nicholas C. Zakas",
"license": "Apache-2.0"
},
{
"name": "@humanfs/node",
- "version": "0.16.6",
+ "version": "0.16.8",
+ "author": "Nicholas C. Zakas",
+ "license": "Apache-2.0"
+ },
+ {
+ "name": "@humanfs/types",
+ "version": "0.15.0",
"author": "Nicholas C. Zakas",
"license": "Apache-2.0"
},
@@ -253,31 +259,31 @@
},
{
"name": "@humanwhocodes/retry",
- "version": "0.3.0",
+ "version": "0.4.3",
"author": "Nicholas C. Zaks",
"license": "Apache-2.0"
},
{
"name": "@intlify/core-base",
- "version": "11.4.6",
+ "version": "11.4.7",
"author": "kazuya kawaguchi",
"license": "MIT"
},
{
"name": "@intlify/devtools-types",
- "version": "11.4.6",
+ "version": "11.4.7",
"author": "kazuya kawaguchi",
"license": "MIT"
},
{
"name": "@intlify/message-compiler",
- "version": "11.4.6",
+ "version": "11.4.7",
"author": "kazuya kawaguchi",
"license": "MIT"
},
{
"name": "@intlify/shared",
- "version": "11.4.6",
+ "version": "11.4.7",
"author": "kazuya kawaguchi",
"license": "MIT"
},
@@ -287,12 +293,6 @@
"author": "Ben Coe",
"license": "ISC"
},
- {
- "name": "@isaacs/cliui",
- "version": "9.0.0",
- "author": "—",
- "license": "BlueOak-1.0.0"
- },
{
"name": "@isaacs/fs-minipass",
"version": "4.0.0",
@@ -341,12 +341,6 @@
"author": "Justin Ridgewell",
"license": "MIT"
},
- {
- "name": "@keyv/serialize",
- "version": "1.1.1",
- "author": "Jared Wray",
- "license": "MIT"
- },
{
"name": "@malept/cross-spawn-promise",
"version": "2.0.0",
@@ -361,19 +355,19 @@
},
{
"name": "@mapbox/jsonlint-lines-primitives",
- "version": "2.0.3",
+ "version": "2.0.2",
"author": "Zach Carter",
- "license": "MIT"
+ "license": "Unknown"
},
{
"name": "@mapbox/unitbezier",
- "version": "1.0.0",
+ "version": "0.0.1",
"author": "—",
"license": "BSD-2-Clause"
},
{
"name": "@maplibre/maplibre-gl-style-spec",
- "version": "24.10.0",
+ "version": "24.4.1",
"author": "MapLibre",
"license": "ISC"
},
@@ -395,6 +389,24 @@
"author": "Paul Miller",
"license": "MIT"
},
+ {
+ "name": "@npmcli/agent",
+ "version": "4.0.2",
+ "author": "GitHub Inc.",
+ "license": "ISC"
+ },
+ {
+ "name": "@npmcli/fs",
+ "version": "5.0.0",
+ "author": "GitHub Inc.",
+ "license": "ISC"
+ },
+ {
+ "name": "@npmcli/redact",
+ "version": "4.0.0",
+ "author": "GitHub Inc.",
+ "license": "ISC"
+ },
{
"name": "@one-ini/wasm",
"version": "0.1.1",
@@ -403,25 +415,25 @@
},
{
"name": "@oxc-parser/binding-linux-x64-gnu",
- "version": "0.137.0",
+ "version": "0.140.0",
"author": "Boshen and oxc contributors",
"license": "MIT"
},
{
"name": "@oxc-project/types",
- "version": "0.133.0",
+ "version": "0.139.0",
"author": "Boshen and oxc contributors",
"license": "MIT"
},
{
"name": "@oxc-resolver/binding-linux-x64-gnu",
- "version": "11.21.3",
+ "version": "11.24.2",
"author": "—",
"license": "MIT"
},
{
"name": "@peculiar/asn1-schema",
- "version": "2.8.0",
+ "version": "2.7.0",
"author": "PeculiarVentures, LLC",
"license": "MIT"
},
@@ -475,7 +487,7 @@
},
{
"name": "@rolldown/binding-linux-x64-gnu",
- "version": "1.0.3",
+ "version": "1.1.5",
"author": "—",
"license": "MIT"
},
@@ -485,15 +497,9 @@
"author": "—",
"license": "MIT"
},
- {
- "name": "@sec-ant/readable-stream",
- "version": "0.4.1",
- "author": "Ze-Zheng Wu",
- "license": "MIT"
- },
{
"name": "@sindresorhus/is",
- "version": "8.1.0",
+ "version": "4.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
@@ -503,6 +509,12 @@
"author": "Colin McDonnell",
"license": "MIT"
},
+ {
+ "name": "@szmarczak/http-timer",
+ "version": "4.0.5",
+ "author": "Szymon Marczak",
+ "license": "MIT"
+ },
{
"name": "@tailwindcss/forms",
"version": "0.5.11",
@@ -511,55 +523,61 @@
},
{
"name": "@tailwindcss/node",
- "version": "4.2.4",
+ "version": "4.3.3",
"author": "—",
"license": "MIT"
},
{
"name": "@tailwindcss/oxide",
- "version": "4.2.4",
+ "version": "4.3.3",
"author": "—",
"license": "MIT"
},
{
"name": "@tailwindcss/oxide-linux-x64-gnu",
- "version": "4.2.4",
+ "version": "4.3.3",
"author": "—",
"license": "MIT"
},
{
"name": "@tailwindcss/vite",
- "version": "4.2.4",
+ "version": "4.3.3",
"author": "—",
"license": "MIT"
},
{
"name": "@tanstack/virtual-core",
- "version": "3.17.2",
+ "version": "3.17.6",
"author": "Tanner Linsley",
"license": "MIT"
},
{
"name": "@tanstack/vue-virtual",
- "version": "3.13.30",
+ "version": "3.13.34",
"author": "Tanner Linsley",
"license": "MIT"
},
+ {
+ "name": "@types/cacheable-request",
+ "version": "6.0.1",
+ "author": "—",
+ "license": "MIT"
+ },
{
"name": "@types/chai",
- "version": "5.2.2",
+ "version": "5.2.3",
"author": "—",
"license": "MIT"
},
{
"name": "@types/debug",
- "version": "4.1.13",
+ "version": "4.1.6",
"author": "—",
"license": "MIT"
},
{
"name": "@types/deep-eql",
- "version": "4.0.0",
+ "version": "4.0.2",
"author": "—",
"license": "MIT"
},
@@ -577,13 +595,13 @@
},
{
"name": "@types/hammerjs",
- "version": "2.0.36",
+ "version": "2.0.46",
"author": "—",
"license": "MIT"
},
{
"name": "@types/http-cache-semantics",
- "version": "4.2.0",
+ "version": "4.0.0",
"author": "—",
"license": "MIT"
},
@@ -594,8 +612,8 @@
"license": "MIT"
},
{
- "name": "@types/ms",
- "version": "2.1.0",
+ "name": "@types/keyv",
+ "version": "3.0.0",
"author": "—",
"license": "MIT"
},
@@ -611,6 +629,12 @@
"author": "—",
"license": "MIT"
},
+ {
+ "name": "@types/responselike",
+ "version": "1.0.0",
+ "author": "—",
+ "license": "MIT"
+ },
{
"name": "@types/trusted-types",
"version": "2.0.7",
@@ -619,61 +643,61 @@
},
{
"name": "@vitejs/plugin-vue",
- "version": "6.0.7",
+ "version": "6.0.8",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vitest/coverage-v8",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "Anthony Fu",
"license": "MIT"
},
{
"name": "@vitest/expect",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
{
"name": "@vitest/mocker",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
{
"name": "@vitest/pretty-format",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
{
"name": "@vitest/runner",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
{
"name": "@vitest/snapshot",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
{
"name": "@vitest/spy",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
{
"name": "@vitest/ui",
- "version": "4.1.9",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
{
"name": "@vitest/utils",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "—",
"license": "MIT"
},
@@ -697,67 +721,67 @@
},
{
"name": "@vue/compiler-core",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/compiler-dom",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/compiler-sfc",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/compiler-ssr",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/devtools-api",
- "version": "6.6.4",
+ "version": "6.5.0",
"author": "Guillaume Chau",
"license": "MIT"
},
{
"name": "@vue/language-core",
- "version": "3.3.6",
+ "version": "3.3.7",
"author": "—",
"license": "MIT"
},
{
"name": "@vue/reactivity",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/runtime-core",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/runtime-dom",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/server-renderer",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "@vue/shared",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
@@ -781,10 +805,16 @@
},
{
"name": "abbrev",
- "version": "2.0.0",
+ "version": "4.0.0",
"author": "GitHub Inc.",
"license": "ISC"
},
+ {
+ "name": "abbrev",
+ "version": "1.0.3",
+ "author": "Isaac Z. Schlueter",
+ "license": "Unknown"
+ },
{
"name": "acorn",
"version": "0.11.0",
@@ -815,6 +845,12 @@
"author": "—",
"license": "MIT"
},
+ {
+ "name": "amdefine",
+ "version": "1.0.1",
+ "author": "James Burke",
+ "license": "BSD-3-Clause OR MIT"
+ },
{
"name": "ansi-regex",
"version": "5.0.1",
@@ -839,27 +875,21 @@
"author": "—",
"license": "Python-2.0"
},
- {
- "name": "array-buffer-byte-length",
- "version": "1.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "arraybuffer.prototype.slice",
- "version": "1.0.4",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "asn1js",
"version": "3.0.10",
"author": "Yury Strozhevsky",
"license": "BSD-3-Clause"
},
+ {
+ "name": "assertion-error",
+ "version": "2.0.1",
+ "author": "Jake Luer",
+ "license": "MIT"
+ },
{
"name": "ast-types",
- "version": "0.8.15",
+ "version": "0.6.12",
"author": "Ben Newman",
"license": "MIT"
},
@@ -871,7 +901,7 @@
},
{
"name": "async",
- "version": "3.2.6",
+ "version": "3.2.3",
"author": "Caolan McMahon",
"license": "MIT"
},
@@ -881,12 +911,6 @@
"author": "Tapani Moilanen",
"license": "MIT"
},
- {
- "name": "async-function",
- "version": "1.0.0",
- "author": "Jordan Harbamd",
- "license": "MIT"
- },
{
"name": "asynckit",
"version": "0.4.0",
@@ -899,12 +923,6 @@
"author": "Ryan Zimmerman",
"license": "ISC"
},
- {
- "name": "available-typed-arrays",
- "version": "1.0.7",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "aws4",
"version": "1.13.2",
@@ -949,7 +967,7 @@
},
{
"name": "boolean",
- "version": "3.2.0",
+ "version": "3.0.1",
"author": "—",
"license": "MIT"
},
@@ -977,51 +995,39 @@
"author": "Vladimir Krivosheev",
"license": "MIT"
},
- {
- "name": "byte-counter",
- "version": "0.1.0",
- "author": "Sindre Sorhus",
- "license": "MIT"
- },
{
"name": "bytestreamjs",
"version": "2.0.1",
"author": "Yury Strozhevsky",
"license": "BSD-3-Clause"
},
+ {
+ "name": "cacache",
+ "version": "20.0.4",
+ "author": "GitHub Inc.",
+ "license": "ISC"
+ },
{
"name": "cacheable-lookup",
- "version": "7.0.0",
+ "version": "5.0.3",
"author": "Szymon Marczak",
"license": "MIT"
},
{
"name": "cacheable-request",
- "version": "13.0.19",
- "author": "Jared Wray",
- "license": "MIT"
- },
- {
- "name": "call-bind",
- "version": "1.0.9",
- "author": "Jordan Harband",
+ "version": "7.0.2",
+ "author": "Luke Childs",
"license": "MIT"
},
{
"name": "call-bind-apply-helpers",
- "version": "1.0.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "call-bound",
- "version": "1.0.4",
+ "version": "1.0.1",
"author": "Jordan Harband",
"license": "MIT"
},
{
"name": "callsites",
- "version": "3.0.0",
+ "version": "3.1.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
@@ -1039,7 +1045,7 @@
},
{
"name": "chalk",
- "version": "4.0.0",
+ "version": "4.1.1",
"author": "—",
"license": "MIT"
},
@@ -1055,15 +1061,9 @@
"author": "—",
"license": "MIT"
},
- {
- "name": "chunk-data",
- "version": "0.1.0",
- "author": "Sindre Sorhus",
- "license": "MIT"
- },
{
"name": "ci-info",
- "version": "4.3.1",
+ "version": "4.2.0",
"author": "Thomas Watson Steen",
"license": "MIT"
},
@@ -1079,6 +1079,12 @@
"author": "Ben Coe",
"license": "ISC"
},
+ {
+ "name": "clone-response",
+ "version": "1.0.2",
+ "author": "Luke Childs",
+ "license": "MIT"
+ },
{
"name": "code-point-at",
"version": "1.0.0",
@@ -1199,24 +1205,6 @@
"author": "Domenic Denicola",
"license": "MIT"
},
- {
- "name": "data-view-buffer",
- "version": "1.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "data-view-byte-length",
- "version": "1.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "data-view-byte-offset",
- "version": "1.0.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "dayjs",
"version": "1.11.21",
@@ -1226,7 +1214,7 @@
{
"name": "debug",
"version": "4.3.1",
- "author": "TJ Holowaychuk",
+ "author": "Josh Junon",
"license": "MIT"
},
{
@@ -1243,25 +1231,25 @@
},
{
"name": "decompress-response",
- "version": "10.0.0",
+ "version": "6.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
{
"name": "deep-is",
- "version": "0.1.3",
+ "version": "0.1.4",
"author": "Thorsten Lorenz",
"license": "MIT"
},
{
- "name": "define-data-property",
- "version": "1.1.4",
- "author": "Jordan Harband",
+ "name": "defer-to-connect",
+ "version": "2.0.0",
+ "author": "Szymon Marczak",
"license": "MIT"
},
{
"name": "define-properties",
- "version": "1.2.1",
+ "version": "1.1.2",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -1279,9 +1267,9 @@
},
{
"name": "detect-node",
- "version": "2.1.0",
+ "version": "2.0.4",
"author": "Ilya Kantor",
- "license": "MIT"
+ "license": "ISC"
},
{
"name": "dijkstrajs",
@@ -1303,7 +1291,7 @@
},
{
"name": "dompurify",
- "version": "3.4.11",
+ "version": "3.4.12",
"author": "Dr.-Ing. Mario Heiderich, Cure53",
"license": "(MPL-2.0 OR Apache-2.0)"
},
@@ -1315,7 +1303,7 @@
},
{
"name": "dotenv-expand",
- "version": "11.0.7",
+ "version": "11.0.6",
"author": "motdotla",
"license": "BSD-2-Clause"
},
@@ -1333,8 +1321,8 @@
},
{
"name": "earcut",
- "version": "3.1.0",
- "author": "Volodymyr Agafonkin",
+ "version": "3.0.0",
+ "author": "Vladimir Agafonkin",
"license": "ISC"
},
{
@@ -1345,7 +1333,7 @@
},
{
"name": "editorconfig",
- "version": "1.0.7",
+ "version": "1.0.3",
"author": "EditorConfig Team",
"license": "MIT"
},
@@ -1409,9 +1397,15 @@
"author": "Mathias Bynens",
"license": "MIT"
},
+ {
+ "name": "end-of-stream",
+ "version": "1.1.0",
+ "author": "Mathias Buus",
+ "license": "MIT"
+ },
{
"name": "enhanced-resolve",
- "version": "5.19.0",
+ "version": "5.24.1",
"author": "Tobias Koppers @sokra",
"license": "MIT"
},
@@ -1429,19 +1423,13 @@
},
{
"name": "err-code",
- "version": "2.0.3",
+ "version": "2.0.2",
"author": "IndigoUnited",
"license": "MIT"
},
{
"name": "es-abstract",
- "version": "1.24.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "es-abstract-get",
- "version": "1.0.0",
+ "version": "1.5.0",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -1459,7 +1447,7 @@
},
{
"name": "es-module-lexer",
- "version": "2.2.0",
+ "version": "2.0.0",
"author": "Guy Bedford",
"license": "MIT"
},
@@ -1477,7 +1465,7 @@
},
{
"name": "es-to-primitive",
- "version": "1.3.4",
+ "version": "1.1.0",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -1501,7 +1489,7 @@
},
{
"name": "eslint",
- "version": "9.39.4",
+ "version": "9.39.5",
"author": "Nicholas C. Zakas",
"license": "MIT"
},
@@ -1525,7 +1513,7 @@
},
{
"name": "eslint-plugin-vue",
- "version": "10.9.2",
+ "version": "10.10.0",
"author": "Toru Nagashima",
"license": "MIT"
},
@@ -1537,7 +1525,7 @@
},
{
"name": "eslint-visitor-keys",
- "version": "3.3.0",
+ "version": "3.4.3",
"author": "Toru Nagashima",
"license": "Apache-2.0"
},
@@ -1555,13 +1543,13 @@
},
{
"name": "esprima-fb",
- "version": "15001.1001.0-dev-harmony-fb",
+ "version": "12001.1.0-dev-harmony-fb",
"author": "Ariya Hidayat",
"license": "BSD"
},
{
"name": "esquery",
- "version": "1.5.0",
+ "version": "1.6.0",
"author": "Joel Feenstra",
"license": "BSD-3-Clause"
},
@@ -1585,9 +1573,9 @@
},
{
"name": "esutils",
- "version": "2.0.2",
+ "version": "2.0.3",
"author": "—",
- "license": "BSD"
+ "license": "BSD-2-Clause"
},
{
"name": "execa",
@@ -1627,13 +1615,13 @@
},
{
"name": "fast-diff",
- "version": "1.3.0",
+ "version": "1.1.2",
"author": "Jason Chen",
"license": "Apache-2.0"
},
{
"name": "fast-json-stable-stringify",
- "version": "2.0.0",
+ "version": "2.1.0",
"author": "James Halliday",
"license": "MIT"
},
@@ -1645,7 +1633,7 @@
},
{
"name": "fast-uri",
- "version": "3.1.2",
+ "version": "4.1.1",
"author": "Vincent Le Goff",
"license": "BSD-3-Clause"
},
@@ -1687,7 +1675,7 @@
},
{
"name": "flat-cache",
- "version": "4.0.0",
+ "version": "4.0.1",
"author": "Jared Wray",
"license": "MIT"
},
@@ -1699,10 +1687,16 @@
},
{
"name": "for-each",
- "version": "0.3.5",
+ "version": "0.3.3",
"author": "Raynos",
"license": "MIT"
},
+ {
+ "name": "foreach",
+ "version": "2.0.6",
+ "author": "Manuel Stofer",
+ "license": "MIT"
+ },
{
"name": "foreground-child",
"version": "3.1.0",
@@ -1727,6 +1721,12 @@
"author": "JP Richardson",
"license": "MIT"
},
+ {
+ "name": "fs-minipass",
+ "version": "3.0.3",
+ "author": "GitHub Inc.",
+ "license": "ISC"
+ },
{
"name": "fs.realpath",
"version": "1.0.0",
@@ -1735,28 +1735,10 @@
},
{
"name": "function-bind",
- "version": "1.1.1",
+ "version": "1.0.2",
"author": "Raynos",
"license": "MIT"
},
- {
- "name": "function.prototype.name",
- "version": "1.2.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "functions-have-names",
- "version": "1.2.3",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "generator-function",
- "version": "2.0.0",
- "author": "Jordan Harbamd",
- "license": "MIT"
- },
{
"name": "geotiff",
"version": "3.0.5",
@@ -1771,13 +1753,7 @@
},
{
"name": "get-intrinsic",
- "version": "1.1.3",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "get-proto",
- "version": "1.0.1",
+ "version": "1.2.6",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -1787,12 +1763,6 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "get-symbol-description",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "get-tsconfig",
"version": "4.14.0",
@@ -1825,19 +1795,19 @@
},
{
"name": "globalthis",
- "version": "1.0.4",
+ "version": "1.0.1",
"author": "Jordan Harband",
"license": "MIT"
},
{
"name": "gopd",
- "version": "1.0.1",
+ "version": "1.2.0",
"author": "Jordan Harband",
"license": "MIT"
},
{
"name": "got",
- "version": "15.0.7",
+ "version": "11.8.6",
"author": "—",
"license": "MIT"
},
@@ -1847,36 +1817,12 @@
"author": "—",
"license": "ISC"
},
- {
- "name": "has",
- "version": "1.0.3",
- "author": "Thiago de Arruda",
- "license": "MIT"
- },
- {
- "name": "has-bigints",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "has-flag",
"version": "4.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "has-property-descriptors",
- "version": "1.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "has-proto",
- "version": "1.2.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "has-symbols",
"version": "1.0.3",
@@ -1891,14 +1837,14 @@
},
{
"name": "hasown",
- "version": "2.0.2",
+ "version": "2.0.4",
"author": "Jordan Harband",
"license": "MIT"
},
{
"name": "hosted-git-info",
- "version": "10.1.1",
- "author": "GitHub Inc.",
+ "version": "2.8.9",
+ "author": "Rebecca Turner",
"license": "ISC"
},
{
@@ -1927,7 +1873,7 @@
},
{
"name": "http2-wrapper",
- "version": "2.2.1",
+ "version": "1.0.0-beta.5.2",
"author": "Szymon Marczak",
"license": "MIT"
},
@@ -1937,9 +1883,15 @@
"author": "Nathan Rajlich",
"license": "MIT"
},
+ {
+ "name": "iconv-lite",
+ "version": "0.7.2",
+ "author": "Alexander Shtuchkin",
+ "license": "MIT"
+ },
{
"name": "ignore",
- "version": "5.2.0",
+ "version": "5.3.2",
"author": "kael",
"license": "MIT"
},
@@ -1951,7 +1903,7 @@
},
{
"name": "import-fresh",
- "version": "3.2.1",
+ "version": "3.3.1",
"author": "Sindre Sorhus",
"license": "MIT"
},
@@ -1991,12 +1943,6 @@
"author": "Calvin W. Metcalf",
"license": "MIT"
},
- {
- "name": "internal-slot",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "invert-kv",
"version": "2.0.0",
@@ -2004,21 +1950,9 @@
"license": "MIT"
},
{
- "name": "is-array-buffer",
- "version": "3.0.5",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-async-function",
- "version": "2.1.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-bigint",
- "version": "1.1.0",
- "author": "Jordan Harband",
+ "name": "ip-address",
+ "version": "9.0.5",
+ "author": "Beau Gunderson",
"license": "MIT"
},
{
@@ -2027,33 +1961,15 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "is-boolean-object",
- "version": "1.2.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "is-callable",
- "version": "1.2.7",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-data-view",
- "version": "1.0.2",
+ "version": "1.1.1",
"author": "Jordan Harband",
"license": "MIT"
},
{
"name": "is-date-object",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-document.all",
- "version": "1.0.0",
+ "version": "1.0.1",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -2063,48 +1979,18 @@
"author": "Jon Schlinkert",
"license": "MIT"
},
- {
- "name": "is-finalizationregistry",
- "version": "1.1.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "is-fullwidth-code-point",
"version": "1.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "is-generator-function",
- "version": "1.1.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "is-glob",
- "version": "4.0.0",
+ "version": "4.0.3",
"author": "Jon Schlinkert",
"license": "MIT"
},
- {
- "name": "is-map",
- "version": "2.0.3",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-negative-zero",
- "version": "2.0.3",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-number-object",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "is-potential-custom-element-name",
"version": "1.0.1",
@@ -2113,19 +1999,7 @@
},
{
"name": "is-regex",
- "version": "1.2.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-set",
- "version": "2.0.3",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-shared-array-buffer",
- "version": "1.0.4",
+ "version": "1.0.3",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -2135,39 +2009,9 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "is-string",
- "version": "1.1.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "is-symbol",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-typed-array",
- "version": "1.1.15",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-weakmap",
- "version": "2.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-weakref",
- "version": "1.1.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "is-weakset",
- "version": "2.0.4",
+ "version": "1.0.1",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -2189,6 +2033,12 @@
"author": "Isaac Z. Schlueter",
"license": "ISC"
},
+ {
+ "name": "isexe",
+ "version": "3.1.5",
+ "author": "Isaac Z. Schlueter",
+ "license": "BlueOak-1.0.0"
+ },
{
"name": "istanbul-lib-coverage",
"version": "3.2.2",
@@ -2227,16 +2077,10 @@
},
{
"name": "js-beautify",
- "version": "1.15.4",
+ "version": "1.14.9",
"author": "Einar Lielmanis",
"license": "MIT"
},
- {
- "name": "js-cookie",
- "version": "3.0.8",
- "author": "Klaus Hartl",
- "license": "MIT"
- },
{
"name": "js-tokens",
"version": "10.0.0",
@@ -2249,6 +2093,12 @@
"author": "Vladimir Zapparov",
"license": "MIT"
},
+ {
+ "name": "jsbn",
+ "version": "1.1.0",
+ "author": "Tom Wu",
+ "license": "MIT"
+ },
{
"name": "jsdom",
"version": "29.1.1",
@@ -2317,13 +2167,13 @@
},
{
"name": "keyv",
- "version": "4.5.4",
+ "version": "4.0.0",
"author": "Jared Wray",
"license": "MIT"
},
{
"name": "knip",
- "version": "6.24.0",
+ "version": "6.29.0",
"author": "Lars Kappert",
"license": "ISC"
},
@@ -2389,7 +2239,7 @@
},
{
"name": "lowercase-keys",
- "version": "3.0.0",
+ "version": "2.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
@@ -2423,6 +2273,12 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
+ {
+ "name": "make-fetch-happen",
+ "version": "15.0.6",
+ "author": "GitHub Inc.",
+ "license": "ISC"
+ },
{
"name": "mapbox-to-css-font",
"version": "3.2.0",
@@ -2431,7 +2287,7 @@
},
{
"name": "marked",
- "version": "18.0.5",
+ "version": "18.0.7",
"author": "Christopher Jeffrey",
"license": "MIT"
},
@@ -2467,7 +2323,7 @@
},
{
"name": "mime",
- "version": "2.6.0",
+ "version": "2.5.2",
"author": "Robert Kieffer",
"license": "MIT"
},
@@ -2485,13 +2341,13 @@
},
{
"name": "mimic-fn",
- "version": "1.2.0",
+ "version": "1.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
{
"name": "mimic-response",
- "version": "4.0.0",
+ "version": "1.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
@@ -2521,20 +2377,44 @@
},
{
"name": "minipass",
- "version": "7.1.2",
+ "version": "3.0.0",
"author": "Isaac Z. Schlueter",
"license": "ISC"
},
{
- "name": "minizlib",
- "version": "3.1.0",
+ "name": "minipass-collect",
+ "version": "2.0.1",
"author": "Isaac Z. Schlueter",
+ "license": "ISC"
+ },
+ {
+ "name": "minipass-fetch",
+ "version": "5.0.2",
+ "author": "GitHub Inc.",
"license": "MIT"
},
{
- "name": "mkdirp",
- "version": "0.5.1",
- "author": "James Halliday",
+ "name": "minipass-flush",
+ "version": "1.0.7",
+ "author": "Isaac Z. Schlueter",
+ "license": "BlueOak-1.0.0"
+ },
+ {
+ "name": "minipass-pipeline",
+ "version": "1.2.4",
+ "author": "Isaac Z. Schlueter",
+ "license": "ISC"
+ },
+ {
+ "name": "minipass-sized",
+ "version": "2.0.0",
+ "author": "Isaac Z. Schlueter",
+ "license": "ISC"
+ },
+ {
+ "name": "minizlib",
+ "version": "3.0.1",
+ "author": "Isaac Z. Schlueter",
"license": "MIT"
},
{
@@ -2567,9 +2447,15 @@
"author": "Lauri Rooden",
"license": "MIT"
},
+ {
+ "name": "negotiator",
+ "version": "1.0.0",
+ "author": "—",
+ "license": "MIT"
+ },
{
"name": "node-abi",
- "version": "4.31.0",
+ "version": "4.2.0",
"author": "Lukas Geiger",
"license": "MIT"
},
@@ -2581,7 +2467,7 @@
},
{
"name": "node-gyp",
- "version": "12.4.0",
+ "version": "12.2.0",
"author": "Nathan Rajlich",
"license": "MIT"
},
@@ -2593,19 +2479,19 @@
},
{
"name": "nopt",
- "version": "7.2.1",
+ "version": "6.0.0",
"author": "GitHub Inc.",
"license": "ISC"
},
{
"name": "normalize-url",
- "version": "8.1.1",
+ "version": "6.0.1",
"author": "Sindre Sorhus",
"license": "MIT"
},
{
"name": "npm-run-path",
- "version": "2.0.2",
+ "version": "2.0.0",
"author": "Sindre Sorhus",
"license": "MIT"
},
@@ -2633,21 +2519,9 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "object-inspect",
- "version": "1.13.4",
- "author": "James Halliday",
- "license": "MIT"
- },
{
"name": "object-keys",
- "version": "1.1.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "object.assign",
- "version": "4.1.7",
+ "version": "1.0.8",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -2671,13 +2545,13 @@
},
{
"name": "once",
- "version": "1.4.0",
+ "version": "1.3.0",
"author": "Isaac Z. Schlueter",
- "license": "ISC"
+ "license": "BSD"
},
{
"name": "optionator",
- "version": "0.9.3",
+ "version": "0.9.4",
"author": "George Zahariev",
"license": "MIT"
},
@@ -2687,24 +2561,24 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "own-keys",
- "version": "1.0.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "oxc-parser",
- "version": "0.137.0",
+ "version": "0.140.0",
"author": "Boshen and oxc contributors",
"license": "MIT"
},
{
"name": "oxc-resolver",
- "version": "11.21.3",
+ "version": "11.24.2",
"author": "—",
"license": "MIT"
},
+ {
+ "name": "p-cancelable",
+ "version": "2.0.0",
+ "author": "Sindre Sorhus",
+ "license": "MIT"
+ },
{
"name": "p-finally",
"version": "1.0.0",
@@ -2729,6 +2603,12 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
+ {
+ "name": "p-map",
+ "version": "7.0.6",
+ "author": "Sindre Sorhus",
+ "license": "MIT"
+ },
{
"name": "p-try",
"version": "2.0.0",
@@ -2755,7 +2635,7 @@
},
{
"name": "parent-module",
- "version": "1.0.0",
+ "version": "1.0.1",
"author": "Sindre Sorhus",
"license": "MIT"
},
@@ -2815,7 +2695,7 @@
},
{
"name": "pe-library",
- "version": "0.4.1",
+ "version": "0.4.0",
"author": "jet",
"license": "MIT"
},
@@ -2861,21 +2741,15 @@
"author": "—",
"license": "MIT"
},
- {
- "name": "possible-typed-array-names",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "postcss",
- "version": "8.5.15",
+ "version": "8.5.19",
"author": "Andrey Sitnik",
"license": "MIT"
},
{
"name": "postcss-selector-parser",
- "version": "7.1.0",
+ "version": "7.1.4",
"author": "—",
"license": "MIT"
},
@@ -2893,7 +2767,7 @@
},
{
"name": "prettier",
- "version": "3.9.3",
+ "version": "3.9.6",
"author": "James Long",
"license": "MIT"
},
@@ -2911,7 +2785,7 @@
},
{
"name": "proc-log",
- "version": "6.1.0",
+ "version": "6.0.0",
"author": "GitHub Inc.",
"license": "ISC"
},
@@ -2941,9 +2815,9 @@
},
{
"name": "proto-list",
- "version": "1.2.4",
+ "version": "1.2.1",
"author": "Isaac Z. Schlueter",
- "license": "ISC"
+ "license": "MIT"
},
{
"name": "protocol-buffers-schema",
@@ -2951,9 +2825,15 @@
"author": "Mathias Buus",
"license": "MIT"
},
+ {
+ "name": "pump",
+ "version": "3.0.0",
+ "author": "Mathias Buus Madsen",
+ "license": "MIT"
+ },
{
"name": "punycode",
- "version": "2.1.0",
+ "version": "2.3.1",
"author": "Mathias Bynens",
"license": "MIT"
},
@@ -2965,7 +2845,7 @@
},
{
"name": "pvutils",
- "version": "1.1.5",
+ "version": "1.1.3",
"author": "Yury Strozhevsky",
"license": "MIT"
},
@@ -3007,7 +2887,7 @@
},
{
"name": "recast",
- "version": "0.10.43",
+ "version": "0.10.1",
"author": "Ben Newman",
"license": "MIT"
},
@@ -3017,24 +2897,12 @@
"author": "manzt",
"license": "MIT"
},
- {
- "name": "reflect.getprototypeof",
- "version": "1.0.10",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "regexp-tree",
"version": "0.1.1",
"author": "Dmitry Soshnikov",
"license": "MIT"
},
- {
- "name": "regexp.prototype.flags",
- "version": "1.5.4",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "require-directory",
"version": "2.1.1",
@@ -3055,13 +2923,13 @@
},
{
"name": "resedit",
- "version": "1.7.2",
+ "version": "1.7.0",
"author": "jet",
"license": "MIT"
},
{
"name": "resolve-alpn",
- "version": "1.2.1",
+ "version": "1.0.0",
"author": "Szymon Marczak",
"license": "MIT"
},
@@ -3085,8 +2953,8 @@
},
{
"name": "responselike",
- "version": "4.0.2",
- "author": "—",
+ "version": "2.0.0",
+ "author": "lukechilds",
"license": "MIT"
},
{
@@ -3103,21 +2971,21 @@
},
{
"name": "roarr",
- "version": "2.15.4",
+ "version": "2.15.3",
"author": "Gajus Kuizinas",
"license": "BSD-3-Clause"
},
{
"name": "rolldown",
- "version": "1.0.3",
+ "version": "1.1.5",
"author": "—",
"license": "MIT"
},
{
- "name": "safe-array-concat",
- "version": "1.1.4",
- "author": "Jordan Harband",
- "license": "MIT"
+ "name": "rw",
+ "version": "1.3.3",
+ "author": "Mike Bostock",
+ "license": "BSD-3-Clause"
},
{
"name": "safe-buffer",
@@ -3125,12 +2993,6 @@
"author": "Feross Aboukhadijeh",
"license": "MIT"
},
- {
- "name": "safe-push-apply",
- "version": "1.0.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "safe-regex",
"version": "2.1.1",
@@ -3138,14 +3000,14 @@
"license": "MIT"
},
{
- "name": "safe-regex-test",
- "version": "1.1.0",
- "author": "Jordan Harband",
+ "name": "safer-buffer",
+ "version": "2.1.2",
+ "author": "Nikita Skovoroda",
"license": "MIT"
},
{
"name": "sanitize-filename",
- "version": "1.6.4",
+ "version": "1.6.3",
"author": "Parsha Pourkhomami",
"license": "WTFPL OR ISC"
},
@@ -3185,24 +3047,6 @@
"author": "Ben Coe",
"license": "ISC"
},
- {
- "name": "set-function-length",
- "version": "1.2.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "set-function-name",
- "version": "2.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "set-proto",
- "version": "1.0.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "setimmediate",
"version": "1.0.5",
@@ -3221,30 +3065,6 @@
"author": "Sindre Sorhus",
"license": "MIT"
},
- {
- "name": "side-channel",
- "version": "1.1.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "side-channel-list",
- "version": "1.0.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "side-channel-map",
- "version": "1.0.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "side-channel-weakmap",
- "version": "1.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "siginfo",
"version": "2.0.0",
@@ -3269,18 +3089,42 @@
"author": "Luke Edwards",
"license": "MIT"
},
+ {
+ "name": "smart-buffer",
+ "version": "4.2.0",
+ "author": "Josh Glazebrook",
+ "license": "MIT"
+ },
{
"name": "smol-toml",
"version": "1.7.0",
"author": "Cynthia Rey",
"license": "BSD-3-Clause"
},
+ {
+ "name": "socks",
+ "version": "2.8.0",
+ "author": "Josh Glazebrook",
+ "license": "MIT"
+ },
+ {
+ "name": "socks-proxy-agent",
+ "version": "8.0.3",
+ "author": "Nathan Rajlich",
+ "license": "MIT"
+ },
{
"name": "source-map",
- "version": "0.5.0",
+ "version": "0.6.0",
"author": "Nick Fitzgerald",
"license": "BSD-3-Clause"
},
+ {
+ "name": "source-map",
+ "version": "0.3.0",
+ "author": "Nick Fitzgerald",
+ "license": "BSD"
+ },
{
"name": "source-map-js",
"version": "1.0.2",
@@ -3295,10 +3139,16 @@
},
{
"name": "sprintf-js",
- "version": "1.1.3",
+ "version": "1.1.2",
"author": "Alexandru Mărășteanu",
"license": "BSD-3-Clause"
},
+ {
+ "name": "ssri",
+ "version": "13.0.1",
+ "author": "GitHub Inc.",
+ "license": "ISC"
+ },
{
"name": "stackback",
"version": "0.0.2",
@@ -3317,12 +3167,6 @@
"author": "—",
"license": "MIT"
},
- {
- "name": "stop-iteration-iterator",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "string-width",
"version": "1.0.1",
@@ -3331,19 +3175,7 @@
},
{
"name": "string.prototype.trim",
- "version": "1.2.11",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "string.prototype.trimend",
- "version": "1.0.10",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "string.prototype.trimstart",
- "version": "1.0.8",
+ "version": "1.1.2",
"author": "Jordan Harband",
"license": "MIT"
},
@@ -3395,33 +3227,27 @@
"author": "JounQin",
"license": "MIT"
},
- {
- "name": "tagged-tag",
- "version": "1.0.0",
- "author": "Sindre Sorhus",
- "license": "MIT"
- },
{
"name": "tailwindcss",
- "version": "4.2.4",
+ "version": "4.3.3",
"author": "—",
"license": "MIT"
},
{
"name": "tapable",
- "version": "2.3.0",
+ "version": "2.3.3",
"author": "Tobias Koppers @sokra",
"license": "MIT"
},
{
"name": "tar",
- "version": "7.5.19",
+ "version": "7.5.21",
"author": "Isaac Z. Schlueter",
"license": "BlueOak-1.0.0"
},
{
"name": "temp",
- "version": "0.9.4",
+ "version": "0.9.0",
"author": "Bruce Williams",
"license": "MIT"
},
@@ -3433,7 +3259,7 @@
},
{
"name": "terser",
- "version": "5.48.0",
+ "version": "5.49.0",
"author": "Mihai Bazon",
"license": "BSD-2-Clause"
},
@@ -3481,13 +3307,13 @@
},
{
"name": "tldts",
- "version": "7.4.5",
+ "version": "7.0.5",
"author": "Rémi Berson",
"license": "MIT"
},
{
"name": "tldts-core",
- "version": "7.4.5",
+ "version": "7.0.5",
"author": "Rémi Berson",
"license": "MIT"
},
@@ -3499,7 +3325,7 @@
},
{
"name": "tmp-promise",
- "version": "3.0.3",
+ "version": "3.0.2",
"author": "Benjamin Gruenbaum and Collaborators.",
"license": "MIT"
},
@@ -3523,13 +3349,13 @@
},
{
"name": "truncate-utf8-bytes",
- "version": "1.0.2",
+ "version": "1.0.0",
"author": "Carl Xiong",
"license": "WTFPL"
},
{
"name": "tslib",
- "version": "2.4.0",
+ "version": "2.8.1",
"author": "Microsoft Corp.",
"license": "0BSD"
},
@@ -3545,54 +3371,18 @@
"author": "Sindre Sorhus",
"license": "(MIT OR CC0-1.0)"
},
- {
- "name": "typed-array-buffer",
- "version": "1.0.3",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "typed-array-byte-length",
- "version": "1.0.3",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "typed-array-byte-offset",
- "version": "1.0.4",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "typed-array-length",
- "version": "1.0.8",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "typescript",
"version": "6.0.3",
"author": "Microsoft Corp.",
"license": "Apache-2.0"
},
- {
- "name": "uint8array-extras",
- "version": "1.5.0",
- "author": "Sindre Sorhus",
- "license": "MIT"
- },
{
"name": "unbash",
- "version": "4.0.2",
+ "version": "4.0.3",
"author": "Lars Kappert",
"license": "ISC"
},
- {
- "name": "unbox-primitive",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "undici",
"version": "7.28.0",
@@ -3625,8 +3415,8 @@
},
{
"name": "unzipper",
- "version": "0.12.5",
- "author": "Ziggy Jonsson",
+ "version": "0.12.3",
+ "author": "Evan Oxfeld",
"license": "MIT"
},
{
@@ -3637,15 +3427,15 @@
},
{
"name": "uri-js",
- "version": "4.2.2",
+ "version": "4.4.1",
"author": "Gary Court",
"license": "BSD-2-Clause"
},
{
"name": "utf8-byte-length",
- "version": "1.0.5",
+ "version": "1.0.1",
"author": "Carl Xiong",
- "license": "(WTFPL OR MIT)"
+ "license": "WTFPL"
},
{
"name": "util-deprecate",
@@ -3679,7 +3469,7 @@
},
{
"name": "vite",
- "version": "8.0.16",
+ "version": "8.1.5",
"author": "Evan You",
"license": "MIT"
},
@@ -3691,25 +3481,25 @@
},
{
"name": "vitest",
- "version": "4.1.5",
+ "version": "4.1.10",
"author": "Anthony Fu",
"license": "MIT"
},
{
"name": "vscode-uri",
- "version": "3.1.0",
+ "version": "3.0.8",
"author": "Microsoft",
"license": "MIT"
},
{
"name": "vue",
- "version": "3.5.39",
+ "version": "3.5.40",
"author": "Evan You",
"license": "MIT"
},
{
"name": "vue-component-type-helpers",
- "version": "3.3.5",
+ "version": "3.0.0",
"author": "—",
"license": "MIT"
},
@@ -3721,7 +3511,7 @@
},
{
"name": "vue-i18n",
- "version": "11.4.6",
+ "version": "11.4.7",
"author": "kazuya kawaguchi",
"license": "MIT"
},
@@ -3733,13 +3523,13 @@
},
{
"name": "vue-tsc",
- "version": "3.3.6",
+ "version": "3.3.7",
"author": "—",
"license": "MIT"
},
{
"name": "vuetify",
- "version": "3.12.8",
+ "version": "3.12.10",
"author": "John Leider",
"license": "MIT"
},
@@ -3791,42 +3581,24 @@
"author": "GitHub Inc.",
"license": "ISC"
},
- {
- "name": "which-boxed-primitive",
- "version": "1.1.0",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "which-builtin-type",
- "version": "1.2.1",
- "author": "Jordan Harband",
- "license": "MIT"
- },
- {
- "name": "which-collection",
- "version": "1.0.2",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "which-module",
"version": "2.0.0",
"author": "nexdrew",
"license": "ISC"
},
- {
- "name": "which-typed-array",
- "version": "1.1.22",
- "author": "Jordan Harband",
- "license": "MIT"
- },
{
"name": "why-is-node-running",
"version": "2.3.0",
"author": "Mathias Buus",
"license": "MIT"
},
+ {
+ "name": "word-wrap",
+ "version": "1.2.5",
+ "author": "Jon Schlinkert",
+ "license": "MIT"
+ },
{
"name": "wrap-ansi",
"version": "2.0.0",
@@ -3835,13 +3607,13 @@
},
{
"name": "wrappy",
- "version": "1.0.2",
+ "version": "1.0.0",
"author": "Isaac Z. Schlueter",
"license": "ISC"
},
{
"name": "xml-name-validator",
- "version": "4.0.0",
+ "version": "5.0.0",
"author": "Domenic Denicola",
"license": "Apache-2.0"
},
diff --git a/meshchatx/src/backend/meshchat_utils.py b/meshchatx/src/backend/meshchat_utils.py
index df37fdb2..7233542a 100644
--- a/meshchatx/src/backend/meshchat_utils.py
+++ b/meshchatx/src/backend/meshchat_utils.py
@@ -50,6 +50,95 @@ def create_lxmf_router(
return LXMF.LXMRouter(**kwargs)
+def list_inbound_deliveries(router) -> list[dict]:
+ """Serialize active inbound LXMF delivery resources (LXMF 1.1+ / RNS 1.4+)."""
+ if router is None or not hasattr(router, "inbound_resources"):
+ return []
+ items: list[dict] = []
+ try:
+ resources = router.inbound_resources() or []
+ except Exception:
+ return []
+ for resource in resources:
+ try:
+ resource_hash = getattr(resource, "hash", None)
+ if resource_hash is None and hasattr(resource, "get_hash"):
+ resource_hash = resource.get_hash()
+ if resource_hash is None:
+ continue
+ if isinstance(resource_hash, (bytes, bytearray)):
+ hash_hex = bytes(resource_hash).hex()
+ else:
+ hash_hex = str(resource_hash)
+ size = None
+ transfer_size = None
+ progress = None
+ with contextlib.suppress(Exception):
+ size = int(resource.get_data_size() or 0)
+ with contextlib.suppress(Exception):
+ transfer_size = int(resource.get_transfer_size() or 0)
+ with contextlib.suppress(Exception):
+ progress_raw = float(resource.get_progress() or 0.0)
+ progress = max(0.0, min(100.0, progress_raw * 100.0))
+ items.append(
+ {
+ "hash": hash_hex,
+ "size_bytes": size,
+ "transfer_size_bytes": transfer_size,
+ "progress": progress,
+ }
+ )
+ except Exception:
+ continue
+ return items
+
+
+def cancel_inbound_deliveries(router, resource_hash: str | None = None) -> dict:
+ """Cancel one or all active inbound LXMF delivery resources.
+
+ Returns a result dict with ok, cancelled count, and optional error.
+ """
+ if router is None:
+ return {"ok": False, "error": "router unavailable", "cancelled": 0}
+
+ cleaned = str(resource_hash or "").strip().lower().replace(":", "")
+ if cleaned:
+ if not hasattr(router, "cancel_inbound"):
+ return {
+ "ok": False,
+ "error": "inbound delivery cancellation is unavailable",
+ "cancelled": 0,
+ }
+ try:
+ hash_bytes = bytes.fromhex(cleaned)
+ except ValueError:
+ return {"ok": False, "error": "invalid resource_hash", "cancelled": 0}
+ if not hash_bytes:
+ return {"ok": False, "error": "invalid resource_hash", "cancelled": 0}
+ try:
+ ok = bool(router.cancel_inbound(hash_bytes))
+ except Exception as exc:
+ return {"ok": False, "error": str(exc), "cancelled": 0}
+ return {
+ "ok": ok,
+ "cancelled": 1 if ok else 0,
+ "resource_hash": cleaned,
+ "error": None if ok else "resource not active",
+ }
+
+ if not hasattr(router, "cancel_all_inbound"):
+ return {
+ "ok": False,
+ "error": "inbound delivery cancellation is unavailable",
+ "cancelled": 0,
+ }
+ try:
+ cancelled = int(router.cancel_all_inbound() or 0)
+ except Exception as exc:
+ return {"ok": False, "error": str(exc), "cancelled": 0}
+ return {"ok": True, "cancelled": cancelled}
+
+
def parse_bool_query_param(value: str | None) -> bool:
if value is None:
return False
diff --git a/meshchatx/src/backend/rns_filesync_handler.py b/meshchatx/src/backend/rns_filesync_handler.py
index 401d7604..270ad85e 100644
--- a/meshchatx/src/backend/rns_filesync_handler.py
+++ b/meshchatx/src/backend/rns_filesync_handler.py
@@ -7,17 +7,21 @@ from __future__ import annotations
import contextlib
import json
import os
+import tempfile
import threading
from collections.abc import Callable
from typing import Any
from rns_filesync.constants import ANNOUNCE_INTERVAL_DEFAULT
-from rns_filesync.paths import PathJailError, normalize_relpath
+from rns_filesync.paths import PathJailError, normalize_relpath, relative_to_root
from rns_filesync.permissions import PermissionStore
from rns_filesync.service import FileSyncService
_ALL_ALIASES = frozenset({"all", "a", "everyone", "*"})
+# Cap for in-app sync-tree uploads (local control plane only).
+MANAGER_UPLOAD_MAX_BYTES = 64 * 1024 * 1024
+
def _normalize_peer_hash(value: str | None) -> str | None:
cleaned = str(value or "").strip().lower().replace(":", "")
@@ -34,6 +38,33 @@ def _normalize_peer_hash(value: str | None) -> str | None:
return cleaned
+def _is_forbidden_entry_name(name: str) -> bool:
+ """Reject hidden and protocol sidecar names in the file manager."""
+ cleaned = str(name or "")
+ if not cleaned or cleaned in (".", ".."):
+ return True
+ if cleaned.startswith("."):
+ return True
+ if cleaned == ".rns-filesync.db" or cleaned.startswith(".rns-filesync"):
+ return True
+ if cleaned.startswith(".rns-xfer"):
+ return True
+ return False
+
+
+def _sanitize_upload_basename(filename: str | None) -> str | None:
+ """Keep only a safe basename for uploads. Fail closed on escape tricks."""
+ raw = str(filename or "").strip()
+ if not raw or "\x00" in raw:
+ return None
+ base = os.path.basename(raw.replace("\\", "/"))
+ if not base or base in (".", "..") or _is_forbidden_entry_name(base):
+ return None
+ if "/" in base or "\\" in base:
+ return None
+ return base
+
+
_RESERVED_SYNC_TOP = frozenset(
{
"identity",
@@ -111,6 +142,359 @@ class RnsFilesyncHandler:
return None
return resolved
+ def _sync_root(self) -> str:
+ """Real path of the configured sync directory (file manager jail base)."""
+ os.makedirs(self._sync_directory, exist_ok=True)
+ return os.path.realpath(self._sync_directory)
+
+ def _is_under_sync_root(self, candidate: str) -> bool:
+ root = self._sync_root()
+ real = os.path.realpath(candidate)
+ return real == root or real.startswith(root + os.sep)
+
+ def _resolve_manager_path(
+ self,
+ path: str | None,
+ *,
+ allow_root: bool = False,
+ must_exist: bool = False,
+ ) -> tuple[str | None, str | None]:
+ """Resolve a path for the sync-root file manager.
+
+ Returns (abspath, error). Fail closed with a generic error string.
+ Empty path with allow_root returns the sync root itself.
+ """
+ root = self._sync_root()
+ cleaned = str(path or "").strip()
+ if not cleaned:
+ if allow_root:
+ return root, None
+ return None, "path is required"
+
+ # Absolute client paths are never accepted for the manager.
+ if "\x00" in cleaned:
+ return None, "path not allowed"
+ if os.path.isabs(cleaned) or cleaned.startswith(("/", "\\")):
+ return None, "path not allowed"
+ if len(cleaned) >= 2 and cleaned[1] == ":":
+ return None, "path not allowed"
+
+ try:
+ safe_rel = normalize_relpath(cleaned)
+ except PathJailError:
+ return None, "path not allowed"
+
+ parts = safe_rel.replace("\\", "/").split("/")
+ if any(_is_forbidden_entry_name(part) for part in parts):
+ return None, "path not allowed"
+
+ joined = os.path.join(root, safe_rel)
+ # Reject symlink parents that escape before realpath of missing leaves.
+ parent = os.path.dirname(joined)
+ if parent != root and not self._is_under_sync_root(parent):
+ return None, "path not allowed"
+ if os.path.lexists(joined) and os.path.islink(joined):
+ real = os.path.realpath(joined)
+ if real != root and not real.startswith(root + os.sep):
+ return None, "path not allowed"
+ if must_exist and not os.path.exists(real):
+ return None, "path not found"
+ return real, None
+
+ if must_exist and not os.path.lexists(joined):
+ return None, "path not found"
+
+ try:
+ real = os.path.realpath(joined)
+ except OSError:
+ return None, "path not allowed"
+
+ if real != root and not real.startswith(root + os.sep):
+ return None, "path not allowed"
+ if not allow_root and real == root:
+ return None, "path not allowed"
+ return real, None
+
+ def _relpath_under_sync(self, abspath: str) -> str | None:
+ try:
+ return relative_to_root(self._sync_root(), abspath)
+ except PathJailError:
+ return None
+
+ def _nudge_inventory(self, relpath: str | None = None) -> None:
+ if self.service is None:
+ return
+ inventory = getattr(self.service, "inventory", None)
+ if inventory is None:
+ return
+ with contextlib.suppress(Exception):
+ if relpath:
+ inventory.update_from_path(relpath)
+ else:
+ inventory.scan()
+
+ def list_tree(self, path: str | None = None) -> dict[str, Any]:
+ """List files and directories under a relative path inside the sync root."""
+ with self._lock:
+ target, err = self._resolve_manager_path(path, allow_root=True)
+ if err or target is None:
+ return {"ok": False, "error": err or "path not allowed"}
+ if not os.path.isdir(target):
+ return {"ok": False, "error": "not a directory"}
+
+ root = self._sync_root()
+ entries: list[dict[str, Any]] = []
+ try:
+ names = sorted(os.listdir(target), key=str.lower)
+ except OSError as exc:
+ return {"ok": False, "error": str(exc)}
+
+ for name in names:
+ if _is_forbidden_entry_name(name):
+ continue
+ full = os.path.join(target, name)
+ if os.path.islink(full):
+ real = os.path.realpath(full)
+ if real != root and not real.startswith(root + os.sep):
+ continue
+ else:
+ real = os.path.realpath(full)
+ if real != root and not real.startswith(root + os.sep):
+ continue
+
+ is_dir = os.path.isdir(real) and not os.path.islink(full)
+ # Treat in-jail symlinks to dirs as dirs for navigation only when target stays inside.
+ if os.path.islink(full) and os.path.isdir(real):
+ is_dir = True
+ rel = self._relpath_under_sync(real)
+ if rel is None and real == root:
+ continue
+ if rel is None:
+ continue
+ item: dict[str, Any] = {
+ "name": name,
+ "path": rel,
+ "type": "dir" if is_dir else "file",
+ }
+ if not is_dir:
+ try:
+ item["size"] = os.path.getsize(real)
+ except OSError:
+ item["size"] = 0
+ entries.append(item)
+
+ current_rel = ""
+ if target != root:
+ current_rel = self._relpath_under_sync(target) or ""
+ parent_rel = None
+ if target != root:
+ parent_abs = os.path.dirname(target)
+ if parent_abs == root:
+ parent_rel = ""
+ else:
+ parent_rel = self._relpath_under_sync(parent_abs)
+
+ return {
+ "ok": True,
+ "root": root,
+ "current": current_rel,
+ "parent": parent_rel,
+ "entries": entries,
+ }
+
+ def manager_mkdir(self, path: str) -> dict[str, Any]:
+ """Create a directory under the sync root (relative path)."""
+ with self._lock:
+ cleaned = str(path or "").strip()
+ if not cleaned:
+ return {"ok": False, "error": "path is required"}
+ # Resolve parent and create leaf so we do not require the leaf to exist.
+ try:
+ safe_rel = normalize_relpath(cleaned)
+ except PathJailError:
+ return {"ok": False, "error": "path not allowed"}
+ parts = safe_rel.replace("\\", "/").split("/")
+ if any(_is_forbidden_entry_name(part) for part in parts):
+ return {"ok": False, "error": "path not allowed"}
+ leaf = parts[-1]
+ parent_rel = "/".join(parts[:-1]) if len(parts) > 1 else ""
+ parent_abs, err = self._resolve_manager_path(
+ parent_rel if parent_rel else None,
+ allow_root=True,
+ must_exist=True,
+ )
+ if err or parent_abs is None:
+ return {"ok": False, "error": err or "path not allowed"}
+ if not os.path.isdir(parent_abs):
+ return {"ok": False, "error": "parent is not a directory"}
+ if os.path.islink(parent_abs):
+ return {"ok": False, "error": "path not allowed"}
+
+ new_path = os.path.join(parent_abs, leaf)
+ if not self._is_under_sync_root(os.path.dirname(new_path)):
+ return {"ok": False, "error": "path not allowed"}
+ if os.path.lexists(new_path):
+ return {"ok": False, "error": "already exists"}
+ try:
+ os.mkdir(new_path)
+ except OSError as exc:
+ return {"ok": False, "error": str(exc)}
+ real = os.path.realpath(new_path)
+ if not self._is_under_sync_root(real):
+ with contextlib.suppress(OSError):
+ os.rmdir(new_path)
+ return {"ok": False, "error": "path not allowed"}
+ rel = self._relpath_under_sync(real) or safe_rel
+ return {"ok": True, "path": rel}
+
+ def manager_upload(
+ self,
+ *,
+ filename: str | None,
+ data: bytes,
+ subdir: str | None = None,
+ ) -> dict[str, Any]:
+ """Write an uploaded file under the sync root."""
+ with self._lock:
+ if not isinstance(data, (bytes, bytearray)):
+ return {"ok": False, "error": "invalid upload data"}
+ if len(data) > MANAGER_UPLOAD_MAX_BYTES:
+ return {"ok": False, "error": "upload too large"}
+ base = _sanitize_upload_basename(filename)
+ if base is None:
+ return {"ok": False, "error": "invalid filename"}
+
+ parent_abs, err = self._resolve_manager_path(
+ subdir,
+ allow_root=True,
+ must_exist=True,
+ )
+ if err or parent_abs is None:
+ return {"ok": False, "error": err or "path not allowed"}
+ if not os.path.isdir(parent_abs) or os.path.islink(parent_abs):
+ return {"ok": False, "error": "path not allowed"}
+
+ dest = os.path.join(parent_abs, base)
+ if os.path.islink(dest):
+ return {"ok": False, "error": "path not allowed"}
+ if os.path.lexists(dest):
+ real_existing = os.path.realpath(dest)
+ if not self._is_under_sync_root(real_existing):
+ return {"ok": False, "error": "path not allowed"}
+
+ tmp_path = None
+ try:
+ fd, tmp_path = tempfile.mkstemp(
+ prefix=".upload-",
+ suffix=".tmp",
+ dir=parent_abs,
+ )
+ try:
+ with os.fdopen(fd, "wb") as handle:
+ handle.write(data)
+ except Exception:
+ with contextlib.suppress(OSError):
+ os.close(fd)
+ raise
+ if not self._is_under_sync_root(tmp_path):
+ with contextlib.suppress(OSError):
+ os.unlink(tmp_path)
+ return {"ok": False, "error": "path not allowed"}
+ os.replace(tmp_path, dest)
+ tmp_path = None
+ except OSError as exc:
+ if tmp_path:
+ with contextlib.suppress(OSError):
+ os.unlink(tmp_path)
+ return {"ok": False, "error": str(exc)}
+
+ real = os.path.realpath(dest)
+ if not self._is_under_sync_root(real) or not os.path.isfile(real):
+ with contextlib.suppress(OSError):
+ os.unlink(dest)
+ return {"ok": False, "error": "path not allowed"}
+ rel = self._relpath_under_sync(real)
+ if rel is None:
+ with contextlib.suppress(OSError):
+ os.unlink(dest)
+ return {"ok": False, "error": "path not allowed"}
+ self._nudge_inventory(rel)
+ return {"ok": True, "path": rel, "size": len(data)}
+
+ def manager_delete(self, path: str) -> dict[str, Any]:
+ """Delete a file or empty directory under the sync root."""
+ with self._lock:
+ root = self._sync_root()
+ try:
+ safe_rel = normalize_relpath(str(path or "").strip())
+ except PathJailError:
+ return {"ok": False, "error": "path not allowed"}
+ lex_path = os.path.join(root, safe_rel)
+ if os.path.islink(lex_path):
+ return {"ok": False, "error": "path not allowed"}
+
+ target, err = self._resolve_manager_path(
+ path,
+ allow_root=False,
+ must_exist=True,
+ )
+ if err or target is None:
+ return {"ok": False, "error": err or "path not allowed"}
+ if target == root:
+ return {"ok": False, "error": "path not allowed"}
+
+ rel = self._relpath_under_sync(target)
+ if rel is None:
+ return {"ok": False, "error": "path not allowed"}
+
+ try:
+ if os.path.isdir(target):
+ try:
+ os.rmdir(target)
+ except OSError:
+ return {"ok": False, "error": "directory is not empty"}
+ elif os.path.isfile(target):
+ os.unlink(target)
+ else:
+ return {"ok": False, "error": "path not found"}
+ except OSError as exc:
+ return {"ok": False, "error": str(exc)}
+
+ self._nudge_inventory(rel)
+ return {"ok": True, "path": rel}
+
+ def manager_content(self, path: str) -> dict[str, Any]:
+ """Resolve a file under the sync root for download streaming."""
+ with self._lock:
+ root = self._sync_root()
+ try:
+ safe_rel = normalize_relpath(str(path or "").strip())
+ except PathJailError:
+ return {"ok": False, "error": "path not allowed"}
+ lex_path = os.path.join(root, safe_rel)
+ if os.path.islink(lex_path):
+ return {"ok": False, "error": "path not allowed"}
+
+ target, err = self._resolve_manager_path(
+ path,
+ allow_root=False,
+ must_exist=True,
+ )
+ if err or target is None:
+ return {"ok": False, "error": err or "path not allowed"}
+ if not os.path.isfile(target):
+ return {"ok": False, "error": "not a file"}
+ rel = self._relpath_under_sync(target)
+ if rel is None:
+ return {"ok": False, "error": "path not allowed"}
+ return {
+ "ok": True,
+ "abspath": target,
+ "path": rel,
+ "filename": os.path.basename(target),
+ "size": os.path.getsize(target),
+ }
+
def _load_settings(self) -> None:
os.makedirs(self._root, exist_ok=True)
if not os.path.isfile(self._settings_path):
diff --git a/meshchatx/src/frontend/components/App.vue b/meshchatx/src/frontend/components/App.vue
index 2b9ed26b..1d3073e0 100644
--- a/meshchatx/src/frontend/components/App.vue
+++ b/meshchatx/src/frontend/components/App.vue
@@ -148,6 +148,15 @@
:class="{ 'animate-spin': isSyncingPropagationNode }"
/>
</button>
+ <button
+ v-if="inboundDeliveryCount > 0"
+ type="button"
+ class="sm:hidden rounded-full p-2 min-h-[44px] min-w-[44px] inline-flex items-center justify-center text-amber-700 dark:text-amber-300 hover:bg-amber-50 dark:hover:bg-amber-900/20 transition-colors"
+ :title="$t('app.cancel_inbound_deliveries')"
+ @click="cancelInboundDeliveries"
+ >
+ <MaterialDesignIcon icon-name="close-circle-outline" class="w-5 h-5" />
+ </button>
<button type="button" class="hidden sm:flex rounded-full" @click="syncPropagationNode">
<span
class="flex text-gray-800 dark:text-zinc-100 bg-white dark:bg-zinc-800/80 border border-gray-200 dark:border-zinc-700 hover:border-blue-400 dark:hover:border-blue-400/60 px-3 py-1.5 rounded-full shadow-xs transition"
@@ -162,6 +171,21 @@
}}</span>
</span>
</button>
+ <button
+ v-if="inboundDeliveryCount > 0"
+ type="button"
+ class="hidden sm:flex rounded-full"
+ @click="cancelInboundDeliveries"
+ >
+ <span
+ class="flex text-amber-800 dark:text-amber-200 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/60 hover:border-amber-400 dark:hover:border-amber-500/60 px-3 py-1.5 rounded-full shadow-xs transition"
+ >
+ <MaterialDesignIcon icon-name="close-circle-outline" class="size-6" />
+ <span class="hidden sm:inline-block my-auto mx-1 text-sm font-medium">{{
+ $t("app.cancel_inbound_deliveries_count", { count: inboundDeliveryCount })
+ }}</span>
+ </span>
+ </button>
<button type="button" class="hidden sm:flex rounded-full" @click="composeNewMessage">
<span
class="flex rounded-full border border-zinc-800 bg-zinc-900 px-3 py-1.5 text-white shadow-xs transition hover:bg-zinc-800 dark:border-zinc-400 dark:bg-zinc-200 dark:text-zinc-900 dark:hover:bg-white"
@@ -646,6 +670,7 @@ import { postRequestPath } from "../js/reticulumPathfinding.js";
import ToneGenerator from "../js/ToneGenerator";
import { listNavItems } from "../js/registries/navRegistry.js";
import { onWsEvent, offWsEvent } from "../js/registries/wsEventRegistry.js";
+import { shouldShowMultiSessionToast } from "../js/activeSessions.js";
import { handleLxmIngestUriResult } from "../js/ingestUriResultNavigation.js";
import { applyRelayShareLink, parseMeshchatRelayUri } from "../js/relayLinkUtils.js";
import logoUrl from "../assets/images/logo.png";
@@ -740,6 +765,7 @@ export default {
identitySwitchDedupeHash: null,
identitySwitchDedupeAt: 0,
shellWsHandlerCleanups: [],
+ multiSessionWarningActive: false,
};
},
computed: {
@@ -782,6 +808,10 @@ export default {
"response_received",
].includes(this.propagationNodeStatus?.state);
},
+ inboundDeliveryCount() {
+ const count = this.propagationNodeStatus?.inbound_delivery_count;
+ return Number.isFinite(Number(count)) ? Math.max(0, Number(count)) : 0;
+ },
activeCallTab() {
return GlobalState.activeCallTab;
},
@@ -1487,6 +1517,18 @@ export default {
this.shellWsHandlerCleanups.push(() => offWsEvent(type, bound));
}
},
+ handleActiveSessionsUpdated(json) {
+ const count = Number(json?.count ?? 0);
+ const warningEnabled =
+ json?.warning_enabled !== undefined
+ ? json.warning_enabled !== false
+ : this.config?.multi_session_warning_enabled !== false;
+ const decision = shouldShowMultiSessionToast(count, warningEnabled, this.multiSessionWarningActive);
+ this.multiSessionWarningActive = decision.warned;
+ if (decision.show) {
+ ToastUtils.warning(this.$t("app.multi_session_warning", { count }));
+ }
+ },
unregisterShellWsHandlers() {
for (const cleanup of this.shellWsHandlerCleanups) {
cleanup();
@@ -1503,6 +1545,9 @@ export default {
this.displayName = next.display_name;
}
},
+ "app.sessions.updated": (json) => {
+ this.handleActiveSessionsUpdated(json);
+ },
keyboard_shortcuts: (json) => {
KeyboardShortcuts.setShortcuts(json.shortcuts);
},
@@ -1994,6 +2039,31 @@ export default {
ToastUtils.dismiss(propagationSyncToastKey);
await this.updatePropagationNodeStatus();
},
+ async cancelInboundDeliveries() {
+ const count = this.inboundDeliveryCount;
+ if (count <= 0) {
+ return;
+ }
+ if (!(await DialogUtils.confirm(this.$t("app.cancel_inbound_confirm", { count })))) {
+ return;
+ }
+ try {
+ const response = await window.api.post("/api/v1/lxmf/propagation-node/cancel-inbound", {});
+ const cancelled = response?.data?.cancelled ?? 0;
+ ToastUtils.success(this.$t("app.cancel_inbound_done", { count: cancelled }));
+ if (response?.data?.inbound_deliveries) {
+ this.propagationNodeStatus = {
+ ...(this.propagationNodeStatus || {}),
+ inbound_delivery_count: response.data.inbound_delivery_count ?? 0,
+ inbound_deliveries: response.data.inbound_deliveries,
+ };
+ } else {
+ await this.updatePropagationNodeStatus();
+ }
+ } catch (e) {
+ ToastUtils.error(e.response?.data?.message ?? this.$t("app.cancel_inbound_failed"));
+ }
+ },
async updatePropagationNodeStatus() {
try {
const response = await window.api.get("/api/v1/lxmf/propagation-node/status");
diff --git a/meshchatx/src/frontend/components/about/AboutPage.vue b/meshchatx/src/frontend/components/about/AboutPage.vue
index 2343373e..5d79c661 100644
--- a/meshchatx/src/frontend/components/about/AboutPage.vue
+++ b/meshchatx/src/frontend/components/about/AboutPage.vue
@@ -358,6 +358,69 @@
</div>
</div>
+ <!-- Active sessions -->
+ <div class="about-section">
+ <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between mb-4">
+ <div
+ class="text-xs font-black text-blue-500 uppercase tracking-[0.2em] flex items-center gap-2"
+ >
+ <v-icon icon="mdi-monitor-multiple" size="14"></v-icon>
+ {{ $t("about.active_sessions") }}
+ </div>
+ <span
+ class="text-[11px] font-black uppercase tracking-wider text-gray-500 dark:text-zinc-400"
+ >
+ {{ $t("about.active_sessions_count", { count: activeSessionCount }) }}
+ </span>
+ </div>
+ <p class="text-[11px] leading-relaxed text-gray-500 dark:text-gray-400 mb-4">
+ {{ $t("about.active_sessions_description") }}
+ </p>
+ <div v-if="!activeSessions.length" class="text-sm text-gray-600 dark:text-zinc-300">
+ {{ $t("about.active_sessions_empty") }}
+ </div>
+ <ul v-else class="space-y-3 list-none">
+ <li
+ v-for="session in activeSessions"
+ :key="session.id"
+ class="rounded-xl border border-gray-200 dark:border-zinc-800 bg-gray-50/70 dark:bg-zinc-900/40 p-3 min-w-0"
+ >
+ <div class="grid gap-2 text-[11px] sm:grid-cols-2">
+ <div class="min-w-0">
+ <div
+ class="text-[10px] font-black uppercase tracking-wider text-gray-400 dark:text-zinc-500 mb-1"
+ >
+ {{ $t("about.active_session_ip") }}
+ </div>
+ <div class="font-mono text-gray-900 dark:text-white break-all">
+ {{ session.ip || "unknown" }}
+ </div>
+ </div>
+ <div class="min-w-0">
+ <div
+ class="text-[10px] font-black uppercase tracking-wider text-gray-400 dark:text-zinc-500 mb-1"
+ >
+ {{ $t("about.active_session_connected") }}
+ </div>
+ <div class="text-gray-900 dark:text-white">
+ {{ formatSessionConnectedAt(session.connected_at) }}
+ </div>
+ </div>
+ <div class="min-w-0 sm:col-span-2">
+ <div
+ class="text-[10px] font-black uppercase tracking-wider text-gray-400 dark:text-zinc-500 mb-1"
+ >
+ {{ $t("about.active_session_user_agent") }}
+ </div>
+ <div class="font-mono text-gray-700 dark:text-zinc-200 break-all">
+ {{ session.user_agent || "unknown" }}
+ </div>
+ </div>
+ </div>
+ </li>
+ </ul>
+ </div>
+
<!-- Advanced Tech Info -->
<div v-if="appInfo" class="about-section">
<div
@@ -366,7 +429,7 @@
<v-icon icon="mdi-server" size="14"></v-icon>
{{ $t("about.environment_information") }}
</div>
- <div class="grid gap-8 sm:grid-cols-2 lg:grid-cols-3 text-sm min-w-0">
+ <div class="grid gap-8 sm:grid-cols-2 text-sm min-w-0">
<div>
<div class="glass-label text-[10px]! mb-2 opacity-50">
{{ $t("about.reticulum_config") }}
@@ -405,111 +468,6 @@
{{ $t("about.reveal_database_file") }}
</button>
</div>
- <div
- class="flex flex-col justify-center space-y-3 py-2 sm:py-3 border-t sm:border border-gray-200/60 dark:border-zinc-800/80 sm:rounded-xl sm:p-4 sm:bg-black/2 dark:sm:bg-white/2"
- >
- <div
- v-if="config"
- class="space-y-3 mb-2 pb-3 border-b border-zinc-100 dark:border-zinc-800"
- >
- <div class="flex flex-col">
- <span class="text-[9px] font-black text-blue-500 uppercase tracking-wider">{{
- $t("about.identity_hash")
- }}</span>
- <span class="font-mono text-[10px] break-all opacity-70">{{
- config.identity_hash
- }}</span>
- </div>
- <div class="flex flex-col">
- <span class="text-[9px] font-black text-blue-500 uppercase tracking-wider">{{
- $t("about.lxmf_address")
- }}</span>
- <span class="font-mono text-[10px] break-all opacity-70">{{
- config.lxmf_address_hash
- }}</span>
- </div>
- </div>
- <div class="flex items-center justify-between">
- <span class="text-[10px] font-black text-blue-500 uppercase tracking-wider">{{
- $t("about.env_python")
- }}</span>
- <span class="font-mono text-xs font-bold"
- >v{{ appInfo.python_version || $t("about.path_unknown") }}</span
- >
- </div>
- <div class="flex items-center justify-between">
- <span class="text-[10px] font-black text-purple-500 uppercase tracking-wider">{{
- $t("about.env_lxmf")
- }}</span>
- <span class="font-mono text-xs font-bold"
- >v{{ appInfo.lxmf_version || $t("about.path_unknown") }}</span
- >
- </div>
- <div class="flex items-center justify-between">
- <span class="text-[10px] font-black text-indigo-500 uppercase tracking-wider">{{
- $t("about.env_rns")
- }}</span>
- <span class="font-mono text-xs font-bold"
- >v{{ appInfo.rns_version || $t("about.path_unknown") }}</span
- >
- </div>
- <div class="flex items-center justify-between">
- <span class="text-[10px] font-black text-emerald-500 uppercase tracking-wider">{{
- $t("about.env_platform")
- }}</span>
- <span class="font-mono text-xs font-bold">{{ environmentInfo.platform }}</span>
- </div>
- <div
- v-if="isLinuxHost && appInfo.landlock_requested !== undefined"
- class="flex flex-col gap-1"
- >
- <div class="flex items-center justify-between gap-3">
- <span class="text-[10px] font-black text-teal-500 uppercase tracking-wider">{{
- $t("app.landlock_status")
- }}</span>
- <span
- class="font-mono text-xs font-bold shrink-0"
- :class="
- appInfo.landlock_active
- ? 'text-green-600 dark:text-green-400'
- : 'text-amber-600 dark:text-amber-400'
- "
- >
- {{
- appInfo.landlock_active
- ? $t("app.landlock_active")
- : $t("app.landlock_inactive")
- }}
- </span>
- </div>
- <p
- v-if="!appInfo.landlock_active && landlockInactiveReason"
- class="text-[10px] leading-snug opacity-70"
- >
- {{ landlockInactiveReason }}
- </p>
- </div>
- <div class="flex items-center justify-between">
- <span class="text-[10px] font-black text-amber-500 uppercase tracking-wider">{{
- $t("about.env_language")
- }}</span>
- <span class="font-mono text-xs font-bold">{{ environmentInfo.language }}</span>
- </div>
- <div class="flex items-center justify-between">
- <span class="text-[10px] font-black text-fuchsia-500 uppercase tracking-wider">{{
- $t("about.env_backend_url")
- }}</span>
- <span class="font-mono text-xs font-bold">{{ environmentInfo.backendUrl }}</span>
- </div>
- <div class="flex flex-col gap-1 pt-2 border-t border-zinc-100 dark:border-zinc-800">
- <span class="text-[10px] font-black text-slate-500 uppercase tracking-wider">{{
- $t("about.env_user_agent")
- }}</span>
- <span class="font-mono text-[10px] break-all opacity-70">{{
- environmentInfo.userAgent
- }}</span>
- </div>
- </div>
</div>
</div>
@@ -1241,6 +1199,7 @@ import DialogUtils from "../../js/DialogUtils";
import ToastUtils from "../../js/ToastUtils";
import DownloadUtils from "../../js/DownloadUtils";
import GlobalEmitter from "../../js/GlobalEmitter";
+import { onWsEvent, offWsEvent } from "../../js/registries/wsEventRegistry.js";
import {
appBatteryUsageToneClass,
batteryStatusIconName,
@@ -1266,7 +1225,6 @@ export default {
appInfo: {
version: "unknown",
},
- config: null,
updateInterval: null,
healthInterval: null,
databaseHealth: null,
@@ -1307,6 +1265,9 @@ export default {
developerLxmfAlternate: "43d3309adf27fc446556121b553b56a6",
moneroDonateAddress:
"83SUg6mmkkVGwCycckLEgRfdmXNm7H9XtVjbGXp5kko71N6pTefYURJeS7WdEGHrz2aagmt4nF3dWg6mHcYs6yu4EokwhTh",
+ activeSessions: [],
+ activeSessionCount: 0,
+ sessionsWsHandler: null,
};
},
computed: {
@@ -1332,21 +1293,6 @@ export default {
return "";
}
},
- isLinuxHost() {
- return this.appInfo && this.appInfo.host_platform === "linux";
- },
- landlockInactiveReason() {
- if (!this.appInfo || this.appInfo.landlock_active) {
- return null;
- }
- if (this.appInfo.landlock_kernel_supported === false) {
- return this.$t("app.landlock_kernel_unsupported");
- }
- if (this.appInfo.landlock_disabled_by_env) {
- return this.$t("app.landlock_disabled_by_env");
- }
- return this.$t("app.landlock_inactive");
- },
batteryStatusIcon() {
return batteryStatusIconName(this.batteryStatus);
},
@@ -1440,43 +1386,10 @@ export default {
}
return "";
},
- environmentInfo() {
- const ua = typeof navigator !== "undefined" ? navigator.userAgent || "" : "";
- let platform = typeof navigator !== "undefined" && navigator.platform ? navigator.platform : "";
- if (
- !platform &&
- typeof navigator !== "undefined" &&
- navigator.userAgentData &&
- navigator.userAgentData.platform
- ) {
- platform = navigator.userAgentData.platform;
- }
- if (!platform && /Android/i.test(ua)) {
- platform = "Android";
- }
- if (!platform && this.appInfo && this.appInfo.host_platform) {
- platform = this.appInfo.host_platform;
- }
- if (!platform) {
- platform = "unknown";
- }
- const language =
- typeof navigator !== "undefined" && navigator.language
- ? navigator.language
- : typeof navigator !== "undefined" && navigator.languages && navigator.languages[0]
- ? navigator.languages[0]
- : "unknown";
- return {
- platform,
- language,
- userAgent: ua || "unknown",
- backendUrl: typeof window !== "undefined" && window.location ? window.location.origin : "unknown",
- };
- },
},
mounted() {
this.getAppInfo();
- this.getConfig();
+ this.getActiveSessions();
this.getDatabaseHealth();
this.listSnapshots();
this.listAutoBackups();
@@ -1485,6 +1398,10 @@ export default {
this.restartAboutPollIntervals();
};
GlobalEmitter.on(BATTERY_SAVER_CHANGED_EVENT, this._batterySaverPrefsHandler);
+ this.sessionsWsHandler = (payload) => {
+ this.applyActiveSessionsPayload(payload);
+ };
+ onWsEvent("app.sessions.updated", this.sessionsWsHandler);
this.restartAboutPollIntervals();
},
beforeUnmount() {
@@ -1497,6 +1414,10 @@ export default {
if (this._batterySaverPrefsHandler) {
GlobalEmitter.off(BATTERY_SAVER_CHANGED_EVENT, this._batterySaverPrefsHandler);
}
+ if (this.sessionsWsHandler) {
+ offWsEvent("app.sessions.updated", this.sessionsWsHandler);
+ this.sessionsWsHandler = null;
+ }
},
methods: {
restartAboutPollIntervals() {
@@ -1510,6 +1431,7 @@ export default {
this.updateInterval = setInterval(
() => {
this.getAppInfo();
+ this.getActiveSessions();
},
applyBackgroundPollInterval(5000, prefs)
);
@@ -1668,6 +1590,31 @@ export default {
console.log(e);
}
},
+ applyActiveSessionsPayload(payload) {
+ const sessions = Array.isArray(payload?.sessions) ? payload.sessions : [];
+ this.activeSessions = sessions;
+ const count = Number(payload?.count);
+ this.activeSessionCount = Number.isFinite(count) ? count : sessions.length;
+ },
+ async getActiveSessions() {
+ try {
+ const response = await window.api.get("/api/v1/app/sessions");
+ this.applyActiveSessionsPayload(response?.data || {});
+ } catch (e) {
+ console.log(e);
+ }
+ },
+ formatSessionConnectedAt(value) {
+ const ts = Number(value);
+ if (!Number.isFinite(ts) || ts <= 0) {
+ return "unknown";
+ }
+ try {
+ return new Date(ts * 1000).toLocaleString();
+ } catch {
+ return "unknown";
+ }
+ },
async refreshBatteryStatus() {
try {
this.batteryStatus = await getDeviceBatteryStatus();
@@ -1818,15 +1765,6 @@ export default {
this.databaseActionInProgress = false;
}
},
- async getConfig() {
- try {
- const response = await window.api.get("/api/v1/config");
- this.config = response.data.config;
- } catch (e) {
- // do nothing if failed to load config
- console.log(e);
- }
- },
async copyValue(value, labelKey) {
if (!value) {
return;
diff --git a/meshchatx/src/frontend/components/filesync/FilesyncFileManager.vue b/meshchatx/src/frontend/components/filesync/FilesyncFileManager.vue
new file mode 100644
index 00000000..dcd6449f
--- /dev/null
+++ b/meshchatx/src/frontend/components/filesync/FilesyncFileManager.vue
@@ -0,0 +1,294 @@
+<!-- SPDX-License-Identifier: 0BSD -->
+
+<template>
+ <div class="space-y-4">
+ <p class="text-sm text-sem-fg-muted">{{ $t("rns_filesync.manager_help") }}</p>
+
+ <div class="flex flex-wrap items-center gap-2">
+ <button
+ type="button"
+ class="secondary-chip px-3 py-1.5 text-sm"
+ :disabled="busy || currentPath === ''"
+ :title="$t('rns_filesync.browser_up')"
+ @click="goUp"
+ >
+ <MaterialDesignIcon icon-name="arrow-up" class="w-4 h-4" />
+ {{ $t("rns_filesync.browser_up") }}
+ </button>
+ <button type="button" class="secondary-chip px-3 py-1.5 text-sm" :disabled="busy" @click="refresh">
+ {{ $t("rns_filesync.refresh") }}
+ </button>
+ <button type="button" class="secondary-chip px-3 py-1.5 text-sm" :disabled="busy" @click="triggerUpload">
+ <MaterialDesignIcon icon-name="upload" class="w-4 h-4" />
+ {{ $t("rns_filesync.upload") }}
+ </button>
+ <button
+ type="button"
+ class="secondary-chip px-3 py-1.5 text-sm"
+ :disabled="busy || !syncDirectory"
+ @click="$emit('open-folder')"
+ >
+ <MaterialDesignIcon icon-name="folder-open-outline" class="w-4 h-4" />
+ {{ $t("rns_filesync.open_folder") }}
+ </button>
+ <input ref="fileInput" type="file" class="hidden" @change="onUploadSelected" />
+ </div>
+
+ <div class="input-field !py-2 font-mono text-xs truncate" :title="breadcrumbLabel">
+ {{ breadcrumbLabel }}
+ </div>
+
+ <div class="flex flex-col sm:flex-row gap-2">
+ <input
+ v-model="newFolderName"
+ type="text"
+ class="input-field flex-1 min-w-0 text-sm"
+ :placeholder="$t('rns_filesync.browser_new_placeholder')"
+ :disabled="busy"
+ @keydown.enter.prevent="createFolder"
+ />
+ <button
+ type="button"
+ class="secondary-chip px-3 py-2 text-sm shrink-0"
+ :disabled="busy || !newFolderName.trim()"
+ @click="createFolder"
+ >
+ <MaterialDesignIcon icon-name="folder-plus-outline" class="w-4 h-4" />
+ {{ $t("rns_filesync.browser_new") }}
+ </button>
+ </div>
+
+ <div v-if="busy && entries.length === 0" class="text-sm text-sem-fg-muted">
+ {{ $t("rns_filesync.manager_loading") }}
+ </div>
+ <div v-else-if="entries.length === 0" class="text-sm text-sem-fg-muted">
+ {{ $t("rns_filesync.manager_empty") }}
+ </div>
+ <ul v-else class="space-y-2">
+ <li
+ v-for="entry in entries"
+ :key="entry.path"
+ class="flex flex-col sm:flex-row sm:items-center justify-between gap-2 p-3 rounded-lg border border-sem-border"
+ >
+ <button
+ v-if="entry.type === 'dir'"
+ type="button"
+ class="min-w-0 flex items-center gap-2 text-left text-sm text-sem-fg hover:text-emerald-600 dark:hover:text-emerald-400"
+ @click="enterDir(entry.path)"
+ >
+ <MaterialDesignIcon
+ icon-name="folder"
+ class="w-5 h-5 shrink-0 text-emerald-600 dark:text-emerald-400"
+ />
+ <span class="break-all">{{ entry.name }}</span>
+ </button>
+ <div v-else class="min-w-0 flex items-center gap-2 text-sm text-sem-fg">
+ <MaterialDesignIcon icon-name="file-outline" class="w-5 h-5 shrink-0 text-sem-fg-muted" />
+ <div class="min-w-0">
+ <div class="break-all">{{ entry.name }}</div>
+ <div class="text-xs text-sem-fg-muted mt-0.5">
+ {{ formatFileSize(entry.size) }}
+ </div>
+ </div>
+ </div>
+ <div class="flex flex-wrap gap-2 shrink-0">
+ <button
+ v-if="entry.type === 'file'"
+ type="button"
+ class="secondary-chip px-3 py-1.5 text-sm"
+ :disabled="busy"
+ @click="downloadEntry(entry)"
+ >
+ {{ $t("rns_filesync.download_local") }}
+ </button>
+ <button
+ type="button"
+ class="secondary-chip px-3 py-1.5 text-sm text-red-600 dark:text-red-300"
+ :disabled="busy"
+ @click="deleteEntry(entry)"
+ >
+ {{ $t("rns_filesync.delete") }}
+ </button>
+ </div>
+ </li>
+ </ul>
+ </div>
+</template>
+
+<script>
+import MaterialDesignIcon from "../MaterialDesignIcon.vue";
+import ToastUtils from "../../js/ToastUtils";
+import DownloadUtils from "../../js/DownloadUtils";
+import DialogUtils from "../../js/DialogUtils";
+import Utils from "../../js/Utils";
+
+export default {
+ name: "FilesyncFileManager",
+ components: {
+ MaterialDesignIcon,
+ },
+ props: {
+ syncDirectory: {
+ type: String,
+ default: "",
+ },
+ },
+ emits: ["open-folder"],
+ data() {
+ return {
+ busy: false,
+ currentPath: "",
+ parentPath: null,
+ entries: [],
+ newFolderName: "",
+ };
+ },
+ computed: {
+ breadcrumbLabel() {
+ if (!this.currentPath) {
+ return this.$t("rns_filesync.manager_root");
+ }
+ return this.currentPath;
+ },
+ },
+ watch: {
+ syncDirectory() {
+ this.currentPath = "";
+ this.refresh();
+ },
+ },
+ mounted() {
+ this.refresh();
+ },
+ methods: {
+ formatFileSize(bytes) {
+ return Utils.formatBytes(bytes || 0);
+ },
+ joinPath(base, name) {
+ const left = String(base || "").replace(/\/+$/, "");
+ const right = String(name || "").replace(/^\/+/, "");
+ if (!left) {
+ return right;
+ }
+ if (!right) {
+ return left;
+ }
+ return `${left}/${right}`;
+ },
+ async refresh() {
+ this.busy = true;
+ try {
+ const params = {};
+ if (this.currentPath) {
+ params.path = this.currentPath;
+ }
+ const response = await window.api.get("/api/v1/filesync/tree", { params });
+ const data = response?.data || {};
+ this.entries = Array.isArray(data.entries) ? data.entries : [];
+ this.currentPath = data.current != null ? String(data.current) : "";
+ this.parentPath = data.parent === undefined ? null : data.parent;
+ } catch (err) {
+ this.entries = [];
+ ToastUtils.error(err?.response?.data?.message || err?.message || this.$t("rns_filesync.error"));
+ } finally {
+ this.busy = false;
+ }
+ },
+ enterDir(path) {
+ this.currentPath = String(path || "");
+ this.refresh();
+ },
+ goUp() {
+ if (this.parentPath === null || this.parentPath === undefined) {
+ return;
+ }
+ this.currentPath = this.parentPath === "" ? "" : String(this.parentPath);
+ this.refresh();
+ },
+ triggerUpload() {
+ this.$refs.fileInput?.click();
+ },
+ async onUploadSelected(event) {
+ const file = event?.target?.files?.[0];
+ if (!file) {
+ return;
+ }
+ this.busy = true;
+ try {
+ const formData = new FormData();
+ formData.append("file", file);
+ if (this.currentPath) {
+ formData.append("path", this.currentPath);
+ }
+ await window.api.post("/api/v1/filesync/upload", formData);
+ ToastUtils.success(this.$t("rns_filesync.upload_done"));
+ await this.refresh();
+ } catch (err) {
+ ToastUtils.error(err?.response?.data?.message || err?.message || this.$t("rns_filesync.error"));
+ } finally {
+ this.busy = false;
+ if (event?.target) {
+ event.target.value = "";
+ }
+ }
+ },
+ async createFolder() {
+ const name = String(this.newFolderName || "").trim();
+ if (!name) {
+ return;
+ }
+ this.busy = true;
+ try {
+ const path = this.joinPath(this.currentPath, name);
+ await window.api.post("/api/v1/filesync/mkdir", { path });
+ ToastUtils.success(this.$t("rns_filesync.browser_created"));
+ this.newFolderName = "";
+ await this.refresh();
+ } catch (err) {
+ ToastUtils.error(err?.response?.data?.message || err?.message || this.$t("rns_filesync.error"));
+ } finally {
+ this.busy = false;
+ }
+ },
+ async downloadEntry(entry) {
+ const path = entry?.path;
+ if (!path) {
+ return;
+ }
+ this.busy = true;
+ try {
+ const response = await window.api.get("/api/v1/filesync/content", {
+ params: { path },
+ responseType: "blob",
+ });
+ await DownloadUtils.downloadFromApiResponse(response, entry.name || "download");
+ ToastUtils.success(this.$t("rns_filesync.download_local_done"));
+ } catch (err) {
+ ToastUtils.error(err?.response?.data?.message || err?.message || this.$t("rns_filesync.error"));
+ } finally {
+ this.busy = false;
+ }
+ },
+ async deleteEntry(entry) {
+ const path = entry?.path;
+ if (!path) {
+ return;
+ }
+ const label = entry.name || path;
+ if (!(await DialogUtils.confirm(this.$t("rns_filesync.delete_confirm", { name: label })))) {
+ return;
+ }
+ this.busy = true;
+ try {
+ await window.api.delete("/api/v1/filesync/entry", { data: { path } });
+ ToastUtils.success(this.$t("rns_filesync.delete_done"));
+ await this.refresh();
+ } catch (err) {
+ ToastUtils.error(err?.response?.data?.message || err?.message || this.$t("rns_filesync.error"));
+ } finally {
+ this.busy = false;
+ }
+ },
+ },
+};
+</script>
diff --git a/meshchatx/src/frontend/components/filesync/RnsFilesyncPage.vue b/meshchatx/src/frontend/components/filesync/RnsFilesyncPage.vue
index 3d5a4677..9801ea46 100644
--- a/meshchatx/src/frontend/components/filesync/RnsFilesyncPage.vue
+++ b/meshchatx/src/frontend/components/filesync/RnsFilesyncPage.vue
@@ -260,37 +260,11 @@
</div>
<div v-else-if="activeTab === 'files'" class="space-y-4">
- <div class="flex flex-wrap items-center gap-2">
- <button
- type="button"
- class="secondary-chip px-3 py-1.5 text-sm"
- :disabled="busy"
- @click="refreshFiles"
- >
- {{ $t("rns_filesync.refresh") }}
- </button>
- <button
- type="button"
- class="secondary-chip px-3 py-1.5 text-sm"
- :disabled="busy || !syncDirectory"
- @click="openSyncFolder"
- >
- <MaterialDesignIcon icon-name="folder-open-outline" class="w-4 h-4" />
- {{ $t("rns_filesync.open_folder") }}
- </button>
- </div>
- <div v-if="files.length === 0" class="text-sm text-sem-fg-muted">
- {{ $t("rns_filesync.no_files") }}
- </div>
- <ul v-else class="space-y-2">
- <li v-for="file in files" :key="file.path" class="p-3 rounded-lg border border-sem-border">
- <div class="break-all text-sm text-sem-fg">{{ file.path }}</div>
- <div class="text-xs text-sem-fg-muted mt-1">
- {{ formatFileSize(file.size) }}
- <span v-if="file.hash" class="font-mono"> · {{ shortHash(file.hash) }}</span>
- </div>
- </li>
- </ul>
+ <FilesyncFileManager
+ ref="fileManager"
+ :sync-directory="syncDirectory"
+ @open-folder="openSyncFolder"
+ />
</div>
<div v-else-if="activeTab === 'remote'" class="space-y-4">
@@ -407,6 +381,7 @@ import MaterialDesignIcon from "../MaterialDesignIcon.vue";
import ToastUtils from "../../js/ToastUtils";
import ToolsPageHeader from "../tools/ToolsPageHeader.vue";
import FilesyncDirectoryBrowserModal from "./FilesyncDirectoryBrowserModal.vue";
+import FilesyncFileManager from "./FilesyncFileManager.vue";
import ElectronUtils from "../../js/ElectronUtils";
import Utils from "../../js/Utils";
import { onWsEvent, offWsEvent } from "../../js/registries/wsEventRegistry.js";
@@ -417,6 +392,7 @@ export default {
MaterialDesignIcon,
ToolsPageHeader,
FilesyncDirectoryBrowserModal,
+ FilesyncFileManager,
},
data() {
return {
@@ -442,7 +418,6 @@ export default {
monitor: true,
connectHash: "",
peers: [],
- files: [],
browsePeerId: "",
remoteFiles: [],
aclEnforce: false,
@@ -546,11 +521,11 @@ export default {
await this.refreshStatus();
});
bind("filesync.file.updated", async () => {
- await this.refreshFiles();
+ await this.refreshFileManager();
ToastUtils.info(this.$t("rns_filesync.file_updated"));
});
bind("filesync.file.deleted", async () => {
- await this.refreshFiles();
+ await this.refreshFileManager();
ToastUtils.info(this.$t("rns_filesync.file_deleted"));
});
bind("filesync.error", (payload) => {
@@ -561,13 +536,6 @@ export default {
formatFileSize(bytes) {
return Utils.formatBytes(bytes || 0);
},
- shortHash(hash) {
- const value = String(hash || "");
- if (value.length <= 12) {
- return value;
- }
- return `${value.slice(0, 8)}...`;
- },
peerStatusLabel(peer) {
const raw = peer?.status;
if (raw === 1 || raw === "connected" || raw === true) {
@@ -606,7 +574,14 @@ export default {
}
},
async refreshAll() {
- await Promise.all([this.refreshStatus(), this.refreshPeers(), this.refreshFiles(), this.refreshAcl()]);
+ await Promise.all([this.refreshStatus(), this.refreshPeers(), this.refreshAcl()]);
+ await this.refreshFileManager();
+ },
+ async refreshFileManager() {
+ const manager = this.$refs.fileManager;
+ if (manager && typeof manager.refresh === "function") {
+ await manager.refresh();
+ }
},
async refreshStatus() {
try {
@@ -635,15 +610,6 @@ export default {
ToastUtils.error(err?.message || this.$t("rns_filesync.error"));
}
},
- async refreshFiles() {
- try {
- const response = await window.api.get("/api/v1/filesync/files");
- const data = response?.data || {};
- this.files = Array.isArray(data.files) ? data.files : [];
- } catch (err) {
- ToastUtils.error(err?.message || this.$t("rns_filesync.error"));
- }
- },
async refreshAcl() {
try {
const response = await window.api.get("/api/v1/filesync/acl");
diff --git a/meshchatx/src/frontend/components/settings/SettingsPage.vue b/meshchatx/src/frontend/components/settings/SettingsPage.vue
index 2729b45f..b68263b7 100644
--- a/meshchatx/src/frontend/components/settings/SettingsPage.vue
+++ b/meshchatx/src/frontend/components/settings/SettingsPage.vue
@@ -2987,6 +2987,22 @@
</span>
</label>
+ <label class="setting-toggle">
+ <Toggle
+ id="multi-session-warning-enabled"
+ v-model="config.multi_session_warning_enabled"
+ @update:model-value="onMultiSessionWarningChange"
+ />
+ <span class="setting-toggle__label">
+ <span class="setting-toggle__title">{{
+ $t("app.multi_session_warning_enabled")
+ }}</span>
+ <span class="setting-toggle__description">{{
+ $t("app.multi_session_warning_description")
+ }}</span>
+ </span>
+ </label>
+
<label class="setting-toggle">
<Toggle
id="obfuscate-hops"
@@ -4020,6 +4036,7 @@ export default {
local_message_auto_delete_value: 30,
local_message_auto_delete_unit: "days",
privacy_mode_enabled: false,
+ multi_session_warning_enabled: true,
},
serverSecurity: {
listen_host: null,
@@ -4885,6 +4902,9 @@ export default {
async onPrivacyModeChange(value) {
await this.updateConfig({ privacy_mode_enabled: value }, "privacy_mode_enabled");
},
+ async onMultiSessionWarningChange(value) {
+ await this.updateConfig({ multi_session_warning_enabled: value }, "multi_session_warning_enabled");
+ },
onWebUiAllowlistChange() {
if (this.saveTimeouts.webUiAllowlist) clearTimeout(this.saveTimeouts.webUiAllowlist);
this.saveTimeouts.webUiAllowlist = setTimeout(async () => {
diff --git a/meshchatx/src/frontend/js/activeSessions.js b/meshchatx/src/frontend/js/activeSessions.js
new file mode 100644
index 00000000..fb7e4da8
--- /dev/null
+++ b/meshchatx/src/frontend/js/activeSessions.js
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: 0BSD
+
+/**
+ * Predict whether the multi-session warning toast should fire.
+ * Mirrors meshchatx.src.backend.active_sessions.should_warn_multi_session.
+ */
+export function shouldWarnMultiSession(count, warningEnabled) {
+ const active = Number(count);
+ if (!Number.isFinite(active)) {
+ return false;
+ }
+ return warningEnabled !== false && active >= 2;
+}
+
+/**
+ * Fire at most once per multi-session episode (count rises to 2+).
+ * Resets when the count drops below 2 so a later episode can warn again.
+ */
+export function shouldShowMultiSessionToast(count, warningEnabled, alreadyWarned) {
+ const shouldWarn = shouldWarnMultiSession(count, warningEnabled);
+ if (!shouldWarn) {
+ return { show: false, warned: false };
+ }
+ if (alreadyWarned) {
+ return { show: false, warned: true };
+ }
+ return { show: true, warned: true };
+}
diff --git a/meshchatx/src/frontend/js/registries/coreSettingsSectionKeywords.js b/meshchatx/src/frontend/js/registries/coreSettingsSectionKeywords.js
index 274b766e..fe3bdc31 100644
--- a/meshchatx/src/frontend/js/registries/coreSettingsSectionKeywords.js
+++ b/meshchatx/src/frontend/js/registries/coreSettingsSectionKeywords.js
@@ -391,6 +391,8 @@ export const CORE_SETTINGS_SECTION_KEYWORDS = {
"app.privacy_data_description",
"app.privacy_mode_enabled",
"app.privacy_mode_description",
+ "app.multi_session_warning_enabled",
+ "app.multi_session_warning_description",
"app.screen_security_enabled",
"app.screen_security_description",
"app.screen_security_drm_eyebrow",
diff --git a/meshchatx/src/frontend/locales/de.json b/meshchatx/src/frontend/locales/de.json
index 4c4dfb09..f3a1e298 100644
--- a/meshchatx/src/frontend/locales/de.json
+++ b/meshchatx/src/frontend/locales/de.json
@@ -138,6 +138,9 @@
"privacy_subsection_telemetry": "Mesh-Telemetrie",
"privacy_mode_enabled": "Datenschutzmodus (externes HTTP/HTTPS blockieren)",
"privacy_mode_description": "Blockiert Kartenkacheln, Geocoding, LibreTranslate, Firmware-Downloads und andere ausgehende HTTP/HTTPS-Verbindungen der App. Die Content Security Policy des Browsers wird auf Same-Origin beschränkt.",
+ "multi_session_warning_enabled": "Bei mehreren verbundenen Sitzungen warnen",
+ "multi_session_warning_description": "Zeigt eine Warnung, wenn zwei oder mehr Browser oder Geräte gleichzeitig mit dieser MeshChatX-Instanz verbunden sind.",
+ "multi_session_warning": "Mehrere aktive Sitzungen ({count}). Ein anderer Browser oder ein anderes Gerät ist mit dieser MeshChatX-Instanz verbunden.",
"screen_security_enabled": "Bildschirmsicherheit (Aufnahme blockieren)",
"screen_security_description": "Verwendet ein Windows-Display-Affinity-Flag, damit MeshChatX in Screenshots, Bildschirmaufnahmen und Windows Recall ausgelassen wird. Das Fenster sieht auf Ihrem Monitor weiterhin normal aus.",
"screen_security_description_short": "Dieses Fenster vor Screenshots, Recordern und Windows Recall verbergen.",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "Eingehende Übertragungen abbrechen",
+ "cancel_inbound_deliveries_count": "Eingehende abbrechen ({count})",
+ "cancel_inbound_confirm": "{count} eingehende LXMF-Übertragung(en) abbrechen? Große Nachrichten, die gerade heruntergeladen werden, werden gestoppt.",
+ "cancel_inbound_done": "{count} eingehende Übertragung(en) abgebrochen.",
+ "cancel_inbound_failed": "Eingehende Übertragungen konnten nicht abgebrochen werden"
},
"common": {
"open": "Öffnen",
@@ -1085,6 +1093,13 @@
"config_path": "Konfigurationspfad",
"database_path": "Datenbankpfad",
"database_size": "Datenbankgröße",
+ "active_sessions": "Aktive Sitzungen",
+ "active_sessions_description": "Browser und Geräte, die derzeit über den UI-WebSocket mit dieser MeshChatX-Instanz verbunden sind.",
+ "active_sessions_empty": "Keine aktiven Sitzungen.",
+ "active_sessions_count": "{count} aktiv",
+ "active_session_ip": "IP-Adresse",
+ "active_session_user_agent": "User-Agent",
+ "active_session_connected": "Verbunden",
"database_health": "Datenbank-Zustand",
"database_health_description": "Schnellprüfung, WAL-Optimierung und Wiederherstellungswerkzeuge für die MeshChatX-Datenbank.",
"running_checks": "Prüfungen werden ausgeführt...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. Unter Geräte die Sync-ID einfügen, um zu verbinden. Lokale Dateien unter Dateien verwalten oder von Remote abrufen.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "Dateien im Sync-Ordner durchsuchen und verwalten. Uploads und Löschungen bleiben nur in diesem Ordner.",
+ "manager_root": "(Wurzel des Sync-Ordners)",
+ "manager_loading": "Dateien werden geladen...",
+ "manager_empty": "Dieser Ordner ist leer. Laden Sie eine Datei hoch oder erstellen Sie einen Unterordner.",
+ "upload": "Hochladen",
+ "upload_done": "Datei hochgeladen",
+ "download_local": "Herunterladen",
+ "download_local_done": "Download gestartet",
+ "delete": "Löschen",
+ "delete_confirm": "„{name}“ aus dem Sync-Ordner löschen?",
+ "delete_done": "Gelöscht"
}
}
diff --git a/meshchatx/src/frontend/locales/en.json b/meshchatx/src/frontend/locales/en.json
index 1ce79cb5..b015e18a 100644
--- a/meshchatx/src/frontend/locales/en.json
+++ b/meshchatx/src/frontend/locales/en.json
@@ -194,6 +194,9 @@
"privacy_subsection_telemetry": "Mesh telemetry",
"privacy_mode_enabled": "Privacy mode (block external HTTP/HTTPS)",
"privacy_mode_description": "Blocks map tiles, geocoding, LibreTranslate, firmware downloads, and other outbound HTTP/HTTPS from the app. The browser Content Security Policy is tightened to same-origin only.",
+ "multi_session_warning_enabled": "Warn when multiple sessions are connected",
+ "multi_session_warning_description": "Show a warning toast when two or more browsers or devices are connected to this MeshChatX instance at the same time.",
+ "multi_session_warning": "Multiple active sessions ({count}). Another browser or device is connected to this MeshChatX instance.",
"screen_security_enabled": "Screen security (block capture)",
"screen_security_description": "Uses a Windows display-affinity flag so MeshChatX is omitted from screenshots, screen recording, and Windows Recall. The window still looks normal on your monitor.",
"screen_security_description_short": "Hide this window from screenshots, recorders, and Windows Recall.",
@@ -384,6 +387,11 @@
"reloaded_rns": "Reticulum reloaded successfully",
"announce_interval": "Announce Interval",
"stop_sync_confirm": "Are you sure you want to stop syncing?",
+ "cancel_inbound_deliveries": "Cancel incoming transfers",
+ "cancel_inbound_deliveries_count": "Cancel incoming ({count})",
+ "cancel_inbound_confirm": "Cancel {count} incoming LXMF delivery transfer(s)? Large messages currently downloading will stop.",
+ "cancel_inbound_done": "Cancelled {count} incoming delivery transfer(s).",
+ "cancel_inbound_failed": "Failed to cancel incoming deliveries",
"sync_error_generic": "Something went wrong. Try again later.",
"sync_complete": "Sync complete. {count} messages received.",
"sync_error": "Sync error: {status}",
@@ -1033,6 +1041,13 @@
"config_path": "Config path",
"database_path": "Database path",
"database_size": "Database size",
+ "active_sessions": "Active sessions",
+ "active_sessions_description": "Browsers and devices currently connected to this MeshChatX instance over the UI WebSocket.",
+ "active_sessions_empty": "No active sessions.",
+ "active_sessions_count": "{count} active",
+ "active_session_ip": "IP address",
+ "active_session_user_agent": "User agent",
+ "active_session_connected": "Connected",
"database_health": "Database Health",
"database_health_description": "Quick check, WAL tuning, and recovery tools for the MeshChatX database.",
"running_checks": "Running checks...",
@@ -3101,7 +3116,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. On Devices, paste their sync ID to connect. Manage local files under Files, or pull from Remote.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3138,6 +3153,17 @@
"disconnect": "Remove",
"no_peers": "No devices connected yet. Start syncing, then paste a friend’s sync ID.",
"no_files": "This folder is empty so far. Drop files in, or pull some from Remote.",
+ "manager_help": "Browse and manage files in your sync folder. Uploads and deletes stay inside this folder only.",
+ "manager_root": "(sync folder root)",
+ "manager_loading": "Loading files...",
+ "manager_empty": "This folder is empty. Upload a file or create a subfolder.",
+ "upload": "Upload",
+ "upload_done": "File uploaded",
+ "download_local": "Download",
+ "download_local_done": "Download started",
+ "delete": "Delete",
+ "delete_confirm": "Delete \"{name}\" from the sync folder?",
+ "delete_done": "Deleted",
"select_peer": "Choose a device",
"browse": "List files",
"no_remote_files": "No remote files yet. Connect a device, then list their files.",
diff --git a/meshchatx/src/frontend/locales/es.json b/meshchatx/src/frontend/locales/es.json
index 1e239638..132a0fd9 100644
--- a/meshchatx/src/frontend/locales/es.json
+++ b/meshchatx/src/frontend/locales/es.json
@@ -164,6 +164,9 @@
"privacy_subsection_telemetry": "Telemetría mesh",
"privacy_mode_enabled": "Modo de privacidad (bloquear HTTP/HTTPS externo)",
"privacy_mode_description": "Bloquea teselas de mapa, geocodificación, LibreTranslate, descargas de firmware y otras conexiones HTTP/HTTPS salientes de la app. La política de seguridad de contenido del navegador se restringe solo al mismo origen.",
+ "multi_session_warning_enabled": "Avisar cuando hay varias sesiones conectadas",
+ "multi_session_warning_description": "Muestra un aviso cuando dos o más navegadores o dispositivos están conectados a esta instancia de MeshChatX al mismo tiempo.",
+ "multi_session_warning": "Varias sesiones activas ({count}). Otro navegador o dispositivo está conectado a esta instancia de MeshChatX.",
"screen_security_enabled": "Seguridad de pantalla (bloquear captura)",
"screen_security_description": "Usa una marca de afinidad de pantalla de Windows para que MeshChatX se omita en capturas, grabaciones y Windows Recall. La ventana sigue viéndose normal en su monitor.",
"screen_security_description_short": "Ocultar esta ventana de capturas, grabadoras y Windows Recall.",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "Cancelar transferencias entrantes",
+ "cancel_inbound_deliveries_count": "Cancelar entrantes ({count})",
+ "cancel_inbound_confirm": "¿Cancelar {count} transferencia(s) entrante(s) de LXMF? Se detendrán los mensajes grandes que se estén descargando.",
+ "cancel_inbound_done": "Se cancelaron {count} transferencia(s) entrante(s).",
+ "cancel_inbound_failed": "No se pudieron cancelar las entregas entrantes"
},
"common": {
"open": "Abierto",
@@ -1033,6 +1041,13 @@
"config_path": "Ruta de confidencialidad",
"database_path": "Vía de base",
"database_size": "Tamaño de la base",
+ "active_sessions": "Sesiones activas",
+ "active_sessions_description": "Navegadores y dispositivos conectados actualmente a esta instancia de MeshChatX por el WebSocket de la interfaz.",
+ "active_sessions_empty": "No hay sesiones activas.",
+ "active_sessions_count": "{count} activas",
+ "active_session_ip": "Dirección IP",
+ "active_session_user_agent": "User agent",
+ "active_session_connected": "Conectada",
"database_health": "Base de datos",
"database_health_description": "Control rápido, ajuste WAL y herramientas de recuperación para la base de datos MeshChatX.",
"running_checks": "Corriendo cheques...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. En Dispositivos, pega su ID de sincronización para conectar. Gestiona archivos locales en Archivos o tráelos desde Remoto.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "Explora y gestiona archivos en tu carpeta de sincronización. Las subidas y eliminaciones se quedan solo en esta carpeta.",
+ "manager_root": "(raíz de la carpeta de sincronización)",
+ "manager_loading": "Cargando archivos...",
+ "manager_empty": "Esta carpeta está vacía. Sube un archivo o crea una subcarpeta.",
+ "upload": "Subir",
+ "upload_done": "Archivo subido",
+ "download_local": "Descargar",
+ "download_local_done": "Descarga iniciada",
+ "delete": "Eliminar",
+ "delete_confirm": "¿Eliminar \"{name}\" de la carpeta de sincronización?",
+ "delete_done": "Eliminado"
}
}
diff --git a/meshchatx/src/frontend/locales/fi.json b/meshchatx/src/frontend/locales/fi.json
index f699e517..82c62367 100644
--- a/meshchatx/src/frontend/locales/fi.json
+++ b/meshchatx/src/frontend/locales/fi.json
@@ -165,6 +165,9 @@
"privacy_subsection_telemetry": "Mesh-verkon käyttötiedot",
"privacy_mode_enabled": "Yksityisyystila (estä ulkoinen HTTP/HTTPS)",
"privacy_mode_description": "Estää karttalaatat, geokoodaukset, LibreTranslate, laiteohjelmistolataukset, ja muut lähtevät HTTP/HTTPS-yhteydet sovelluksesta. Selaimen sisällön turvallisuuskäytäntö (CSP) kiristetään sallimaan vain samasta lähteestä oleva liikenne.",
+ "multi_session_warning_enabled": "Varoita, kun useampi istunto on yhteydessä",
+ "multi_session_warning_description": "Näytä varoitus, kun kaksi tai useampi selain tai laite on yhdistetty tähän MeshChatX-instanssiin samaan aikaan.",
+ "multi_session_warning": "Useita aktiivisia istuntoja ({count}). Toinen selain tai laite on yhteydessä tähän MeshChatX-instanssiin.",
"screen_security_enabled": "Näytön suojaus (estä kaappaus)",
"screen_security_description": "Käyttää Windowsin display-affinity-lippua, jotta MeshChatX jätetään pois kuvakaappauksista, näytön tallennuksesta ja Windows Recallista. Ikkuna näyttää edelleen normaalilta näytölläsi.",
"screen_security_description_short": "Piilota tämä ikkuna kuvakaappauksilta, tallentimilta ja Windows Recallilta.",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "Peruuta saapuvat siirrot",
+ "cancel_inbound_deliveries_count": "Peruuta saapuvat ({count})",
+ "cancel_inbound_confirm": "Perutaanko {count} saapuvaa LXMF-siirtoa? Suurten, juuri ladattavien viestien lataus pysähtyy.",
+ "cancel_inbound_done": "Peruttiin {count} saapuvaa siirtoa.",
+ "cancel_inbound_failed": "Saapuvia siirtoja ei voitu perua"
},
"common": {
"open": "Avaa",
@@ -1033,6 +1041,13 @@
"config_path": "Kokoonpanon polku",
"database_path": "Tietokannan polku",
"database_size": "Tietokannan koko",
+ "active_sessions": "Aktiiviset istunnot",
+ "active_sessions_description": "Selaimet ja laitteet, jotka ovat tällä hetkellä yhteydessä tähän MeshChatX-instanssiin UI-WebSocketin kautta.",
+ "active_sessions_empty": "Ei aktiivisia istuntoja.",
+ "active_sessions_count": "{count} aktiivista",
+ "active_session_ip": "IP-osoite",
+ "active_session_user_agent": "User agent",
+ "active_session_connected": "Yhdistetty",
"database_health": "Tietokannan eheys",
"database_health_description": "MeshChatX-tietokannan tarkistus, WAL-säätö ja palautustyökalut.",
"running_checks": "Tarkistetaan...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. Liitä laitteissa heidän synkronointitunnuksensa yhteyden muodostamiseksi. Hallitse paikallisia tiedostoja Kohdassa Tiedostot tai hae etäyhteydestä.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "Selaa ja hallitse tiedostoja synkronointikansiossa. Lähetykset ja poistot pysyvät vain tässä kansiossa.",
+ "manager_root": "(synkronointikansion juuri)",
+ "manager_loading": "Ladataan tiedostoja...",
+ "manager_empty": "Tämä kansio on tyhjä. Lähetä tiedosto tai luo alikansio.",
+ "upload": "Lähetä",
+ "upload_done": "Tiedosto lähetetty",
+ "download_local": "Lataa",
+ "download_local_done": "Lataus aloitettu",
+ "delete": "Poista",
+ "delete_confirm": "Poistetaanko \"{name}\" synkronointikansiosta?",
+ "delete_done": "Poistettu"
}
}
diff --git a/meshchatx/src/frontend/locales/fr.json b/meshchatx/src/frontend/locales/fr.json
index f7a99827..63d5dd76 100644
--- a/meshchatx/src/frontend/locales/fr.json
+++ b/meshchatx/src/frontend/locales/fr.json
@@ -164,6 +164,9 @@
"privacy_subsection_telemetry": "Télémétrie mesh",
"privacy_mode_enabled": "Mode confidentialité (bloquer HTTP/HTTPS externe)",
"privacy_mode_description": "Bloque les tuiles de carte, le géocodage, LibreTranslate, les téléchargements de firmware et autres connexions HTTP/HTTPS sortantes de l'application. La politique de sécurité du contenu du navigateur est restreinte à same-origin uniquement.",
+ "multi_session_warning_enabled": "Avertir lorsque plusieurs sessions sont connectées",
+ "multi_session_warning_description": "Affiche un avertissement lorsque deux navigateurs ou appareils ou plus sont connectés à cette instance MeshChatX en même temps.",
+ "multi_session_warning": "Plusieurs sessions actives ({count}). Un autre navigateur ou appareil est connecté à cette instance MeshChatX.",
"screen_security_enabled": "Sécurité d’écran (bloquer la capture)",
"screen_security_description": "Utilise un indicateur d’affinité d’affichage Windows pour que MeshChatX soit omis des captures d’écran, de l’enregistrement et de Windows Recall. La fenêtre reste normale sur votre moniteur.",
"screen_security_description_short": "Masquer cette fenêtre des captures, enregistreurs et de Windows Recall.",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "Annuler les transferts entrants",
+ "cancel_inbound_deliveries_count": "Annuler entrants ({count})",
+ "cancel_inbound_confirm": "Annuler {count} transfert(s) entrant(s) LXMF ? Les grands messages en cours de téléchargement seront arrêtés.",
+ "cancel_inbound_done": "{count} transfert(s) entrant(s) annulé(s).",
+ "cancel_inbound_failed": "Échec de l'annulation des livraisons entrantes"
},
"common": {
"open": "Ouvrir",
@@ -1033,6 +1041,13 @@
"config_path": "Configurer le chemin",
"database_path": "Chemin de la base de données",
"database_size": "Taille de la base de données",
+ "active_sessions": "Sessions actives",
+ "active_sessions_description": "Navigateurs et appareils actuellement connectés à cette instance MeshChatX via le WebSocket de l'interface.",
+ "active_sessions_empty": "Aucune session active.",
+ "active_sessions_count": "{count} active(s)",
+ "active_session_ip": "Adresse IP",
+ "active_session_user_agent": "User-agent",
+ "active_session_connected": "Connectée",
"database_health": "Base de données Santé",
"database_health_description": "Vérification rapide, réglage WAL et outils de récupération pour la base de données MeshChatX.",
"running_checks": "Des contrôles...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. Dans Appareils, collez leur ID de synchronisation pour vous connecter. Gérez les fichiers locaux sous Fichiers, ou récupérez-les depuis Distant.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "Parcourez et gérez les fichiers de votre dossier de synchronisation. Les envois et suppressions restent uniquement dans ce dossier.",
+ "manager_root": "(racine du dossier de synchronisation)",
+ "manager_loading": "Chargement des fichiers...",
+ "manager_empty": "Ce dossier est vide. Envoyez un fichier ou créez un sous-dossier.",
+ "upload": "Envoyer",
+ "upload_done": "Fichier envoyé",
+ "download_local": "Télécharger",
+ "download_local_done": "Téléchargement démarré",
+ "delete": "Supprimer",
+ "delete_confirm": "Supprimer « {name} » du dossier de synchronisation ?",
+ "delete_done": "Supprimé"
}
}
diff --git a/meshchatx/src/frontend/locales/it.json b/meshchatx/src/frontend/locales/it.json
index 3e876fa1..c26c7f98 100644
--- a/meshchatx/src/frontend/locales/it.json
+++ b/meshchatx/src/frontend/locales/it.json
@@ -164,6 +164,9 @@
"privacy_subsection_telemetry": "Telemetria mesh",
"privacy_mode_enabled": "Modalità privacy (blocca HTTP/HTTPS esterni)",
"privacy_mode_description": "Blocca tile mappe, geocodifica, LibreTranslate, download firmware e altre connessioni HTTP/HTTPS in uscita dall'app. La Content Security Policy del browser è ristretta al solo same-origin.",
+ "multi_session_warning_enabled": "Avvisa quando sono collegate più sessioni",
+ "multi_session_warning_description": "Mostra un avviso quando due o più browser o dispositivi sono collegati a questa istanza MeshChatX contemporaneamente.",
+ "multi_session_warning": "Più sessioni attive ({count}). Un altro browser o dispositivo è collegato a questa istanza MeshChatX.",
"screen_security_enabled": "Sicurezza schermo (blocca acquisizione)",
"screen_security_description": "Usa un flag di affinità display di Windows affinché MeshChatX sia escluso da screenshot, registrazioni e Windows Recall. La finestra resta normale sul monitor.",
"screen_security_description_short": "Nascondi questa finestra da screenshot, registratori e Windows Recall.",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "Annulla trasferimenti in arrivo",
+ "cancel_inbound_deliveries_count": "Annulla in arrivo ({count})",
+ "cancel_inbound_confirm": "Annullare {count} trasferimento/i LXMF in arrivo? I messaggi grandi in download verranno interrotti.",
+ "cancel_inbound_done": "Annullati {count} trasferimento/i in arrivo.",
+ "cancel_inbound_failed": "Impossibile annullare le consegne in arrivo"
},
"common": {
"open": "Apri",
@@ -1085,6 +1093,13 @@
"config_path": "Percorso config",
"database_path": "Percorso database",
"database_size": "Dimensione database",
+ "active_sessions": "Sessioni attive",
+ "active_sessions_description": "Browser e dispositivi attualmente connessi a questa istanza MeshChatX tramite il WebSocket dell'interfaccia.",
+ "active_sessions_empty": "Nessuna sessione attiva.",
+ "active_sessions_count": "{count} attive",
+ "active_session_ip": "Indirizzo IP",
+ "active_session_user_agent": "User agent",
+ "active_session_connected": "Connessa",
"database_health": "Salute Database",
"database_health_description": "Controllo rapido, ottimizzazione WAL e strumenti di recupero per il database MeshChatX.",
"running_checks": "Esecuzione controlli...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. In Dispositivi, incolla il loro ID di sincronizzazione per connetterti. Gestisci i file locali in File, oppure scaricali da Remoto.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "Sfoglia e gestisci i file nella cartella di sincronizzazione. Caricamenti ed eliminazioni restano solo in questa cartella.",
+ "manager_root": "(radice della cartella di sincronizzazione)",
+ "manager_loading": "Caricamento file...",
+ "manager_empty": "Questa cartella è vuota. Carica un file o crea una sottocartella.",
+ "upload": "Carica",
+ "upload_done": "File caricato",
+ "download_local": "Scarica",
+ "download_local_done": "Download avviato",
+ "delete": "Elimina",
+ "delete_confirm": "Eliminare \"{name}\" dalla cartella di sincronizzazione?",
+ "delete_done": "Eliminato"
}
}
diff --git a/meshchatx/src/frontend/locales/nl.json b/meshchatx/src/frontend/locales/nl.json
index 0bba82b7..ab36e264 100644
--- a/meshchatx/src/frontend/locales/nl.json
+++ b/meshchatx/src/frontend/locales/nl.json
@@ -164,6 +164,9 @@
"privacy_subsection_telemetry": "Mesh-telemetrie",
"privacy_mode_enabled": "Privacymodus (blokkeer extern HTTP/HTTPS)",
"privacy_mode_description": "Blokkeert kaarttegels, geocodering, LibreTranslate, firmware-downloads en andere uitgaande HTTP/HTTPS-verbindingen van de app. Het Content Security Policy van de browser wordt beperkt tot same-origin.",
+ "multi_session_warning_enabled": "Waarschuwen bij meerdere verbonden sessies",
+ "multi_session_warning_description": "Toon een waarschuwing wanneer twee of meer browsers of apparaten tegelijk met deze MeshChatX-instantie zijn verbonden.",
+ "multi_session_warning": "Meerdere actieve sessies ({count}). Een andere browser of een ander apparaat is verbonden met deze MeshChatX-instantie.",
"screen_security_enabled": "Schermbeveiliging (opname blokkeren)",
"screen_security_description": "Gebruikt een Windows display-affinity-vlag zodat MeshChatX wordt weggelaten uit screenshots, schermopnames en Windows Recall. Het venster ziet er op je monitor nog steeds normaal uit.",
"screen_security_description_short": "Verberg dit venster voor screenshots, recorders en Windows Recall.",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "Inkomende overdrachten annuleren",
+ "cancel_inbound_deliveries_count": "Inkomend annuleren ({count})",
+ "cancel_inbound_confirm": "{count} inkomende LXMF-overdracht(en) annuleren? Grote berichten die nu worden gedownload stoppen.",
+ "cancel_inbound_done": "{count} inkomende overdracht(en) geannuleerd.",
+ "cancel_inbound_failed": "Inkomende leveringen konden niet worden geannuleerd"
},
"common": {
"open": "Openen",
@@ -1033,6 +1041,13 @@
"config_path": "Pad instellen",
"database_path": "Databasepad",
"database_size": "Databasegrootte",
+ "active_sessions": "Actieve sessies",
+ "active_sessions_description": "Browsers en apparaten die momenteel via de UI-WebSocket met deze MeshChatX-instantie zijn verbonden.",
+ "active_sessions_empty": "Geen actieve sessies.",
+ "active_sessions_count": "{count} actief",
+ "active_session_ip": "IP-adres",
+ "active_session_user_agent": "User agent",
+ "active_session_connected": "Verbonden",
"database_health": "Databasegezondheid",
"database_health_description": "Snel controleren, WAL tuning, en herstel tools voor de MeshChatX database.",
"running_checks": "Controles uitvoeren...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. Plak op Apparaten hun sync-ID om te verbinden. Beheer lokale bestanden onder Bestanden, of haal ze van Extern.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "Blader door en beheer bestanden in je syncmap. Uploads en verwijderingen blijven alleen in deze map.",
+ "manager_root": "(hoofdmap van de syncmap)",
+ "manager_loading": "Bestanden laden...",
+ "manager_empty": "Deze map is leeg. Upload een bestand of maak een submap.",
+ "upload": "Uploaden",
+ "upload_done": "Bestand geüpload",
+ "download_local": "Downloaden",
+ "download_local_done": "Download gestart",
+ "delete": "Verwijderen",
+ "delete_confirm": "\"{name}\" uit de syncmap verwijderen?",
+ "delete_done": "Verwijderd"
}
}
diff --git a/meshchatx/src/frontend/locales/ru.json b/meshchatx/src/frontend/locales/ru.json
index 04b9e791..f0717e92 100644
--- a/meshchatx/src/frontend/locales/ru.json
+++ b/meshchatx/src/frontend/locales/ru.json
@@ -138,6 +138,9 @@
"privacy_subsection_telemetry": "Телеметрия mesh",
"privacy_mode_enabled": "Режим конфиденциальности (блокировка внешнего HTTP/HTTPS)",
"privacy_mode_description": "Блокирует тайлы карт, геокодирование, LibreTranslate, загрузку прошивок и другие исходящие HTTP/HTTPS-соединения приложения. Политика безопасности контента браузера ограничивается только same-origin.",
+ "multi_session_warning_enabled": "Предупреждать при нескольких подключённых сеансах",
+ "multi_session_warning_description": "Показывать предупреждение, когда к этому экземпляру MeshChatX одновременно подключены два или более браузера или устройства.",
+ "multi_session_warning": "Несколько активных сеансов ({count}). К этому экземпляру MeshChatX подключён другой браузер или устройство.",
"screen_security_enabled": "Защита экрана (блокировать захват)",
"screen_security_description": "Использует флаг display affinity Windows, чтобы MeshChatX не попадал в снимки экрана, запись и Windows Recall. Окно по-прежнему выглядит обычно на мониторе.",
"screen_security_description_short": "Скрыть это окно от снимков экрана, записи и Windows Recall.",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "Отменить входящие передачи",
+ "cancel_inbound_deliveries_count": "Отменить входящие ({count})",
+ "cancel_inbound_confirm": "Отменить {count} входящую LXMF-передачу(и)? Крупные сообщения, которые сейчас загружаются, будут остановлены.",
+ "cancel_inbound_done": "Отменено входящих передач: {count}.",
+ "cancel_inbound_failed": "Не удалось отменить входящие доставки"
},
"common": {
"open": "Открыть",
@@ -1085,6 +1093,13 @@
"config_path": "Путь к конфигу",
"database_path": "Путь к базе данных",
"database_size": "Размер базы данных",
+ "active_sessions": "Активные сеансы",
+ "active_sessions_description": "Браузеры и устройства, подключённые к этому экземпляру MeshChatX через UI WebSocket.",
+ "active_sessions_empty": "Нет активных сеансов.",
+ "active_sessions_count": "{count} активных",
+ "active_session_ip": "IP-адрес",
+ "active_session_user_agent": "User-Agent",
+ "active_session_connected": "Подключён",
"database_health": "Состояние базы данных",
"database_health_description": "Быстрая проверка, настройка WAL и инструменты восстановления базы данных MeshChatX.",
"running_checks": "Выполнение проверок...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. На вкладке Устройства вставьте их ID синхронизации для подключения. Управляйте локальными файлами в разделе Файлы или загружайте с Удалённых.",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "Просматривайте и управляйте файлами в папке синхронизации. Загрузки и удаления остаются только в этой папке.",
+ "manager_root": "(корень папки синхронизации)",
+ "manager_loading": "Загрузка файлов...",
+ "manager_empty": "Эта папка пуста. Загрузите файл или создайте подпапку.",
+ "upload": "Загрузить",
+ "upload_done": "Файл загружен",
+ "download_local": "Скачать",
+ "download_local_done": "Скачивание начато",
+ "delete": "Удалить",
+ "delete_confirm": "Удалить «{name}» из папки синхронизации?",
+ "delete_done": "Удалено"
}
}
diff --git a/meshchatx/src/frontend/locales/zh.json b/meshchatx/src/frontend/locales/zh.json
index cdf16bdf..6786f75d 100644
--- a/meshchatx/src/frontend/locales/zh.json
+++ b/meshchatx/src/frontend/locales/zh.json
@@ -164,6 +164,9 @@
"privacy_subsection_telemetry": "Mesh 遥测",
"privacy_mode_enabled": "隐私模式(阻止外部 HTTP/HTTPS)",
"privacy_mode_description": "阻止地图瓦片、地理编码、LibreTranslate、固件下载以及应用的其他出站 HTTP/HTTPS 连接。浏览器内容安全策略收紧为仅同源。",
+ "multi_session_warning_enabled": "多个会话连接时发出警告",
+ "multi_session_warning_description": "当两个或更多浏览器或设备同时连接到此 MeshChatX 实例时显示警告提示。",
+ "multi_session_warning": "多个活动会话({count})。另一个浏览器或设备已连接到此 MeshChatX 实例。",
"screen_security_enabled": "屏幕安全(阻止截录)",
"screen_security_description": "使用 Windows 显示亲和性标志,使 MeshChatX 不出现在截图、录屏和 Windows Recall 中。窗口在显示器上仍正常显示。",
"screen_security_description_short": "对此窗口隐藏截图、录屏和 Windows Recall。",
@@ -507,7 +510,12 @@
"propagation_static_peers_bypass_sequential": "Static Peers Bypass Sequential Validation",
"propagation_static_peers_bypass_sequential_description": "Allow configured static peers to sync immediately even when another stamp batch is already validating.",
"propagation_max_inbound_syncs": "Max Concurrent Inbound Syncs",
- "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3."
+ "propagation_max_inbound_syncs_description": "How many concurrent inbound propagation sync transfers to accept. Extra offers are throttled. Range: 1-64. Default: 3.",
+ "cancel_inbound_deliveries": "取消传入传输",
+ "cancel_inbound_deliveries_count": "取消传入({count})",
+ "cancel_inbound_confirm": "取消 {count} 个传入 LXMF 传输?正在下载的大消息将停止。",
+ "cancel_inbound_done": "已取消 {count} 个传入传输。",
+ "cancel_inbound_failed": "无法取消传入投递"
},
"common": {
"open": "打开",
@@ -1033,6 +1041,13 @@
"config_path": "配置路径",
"database_path": "数据库路径",
"database_size": "数据库大小",
+ "active_sessions": "活动会话",
+ "active_sessions_description": "当前通过 UI WebSocket 连接到此 MeshChatX 实例的浏览器和设备。",
+ "active_sessions_empty": "没有活动会话。",
+ "active_sessions_count": "{count} 个活动",
+ "active_session_ip": "IP 地址",
+ "active_session_user_agent": "用户代理",
+ "active_session_connected": "已连接",
"database_health": "数据库健康",
"database_health_description": "MeshChatX 数据库的缓存、WAL 调优和恢复工具。",
"running_checks": "正在检查...",
@@ -3828,7 +3843,7 @@
"description": "Keep a folder in sync with people on your mesh. Share your sync ID, connect devices, and files move both ways when allowed.",
"usage_steps": "Quick start",
"step_1": "1. Choose a folder, press Start, then share your sync ID with a friend.",
- "step_2": "2. On Devices, paste their sync ID to connect. Files appear under Files as they sync.",
+ "step_2": "2. 在“设备”中粘贴对方的同步 ID 以连接。在“文件”中管理本地文件,或从“远程”拉取。",
"step_3": "3. Under Sharing, decide who can read, write, or delete when access control is on.",
"tab_folder": "Folder",
"tab_devices": "Devices",
@@ -3906,6 +3921,17 @@
"file_updated": "A file changed",
"file_deleted": "A file was removed",
"copied": "Copied to clipboard",
- "error": "File Sync error"
+ "error": "File Sync error",
+ "manager_help": "浏览并管理同步文件夹中的文件。上传和删除仅限于此文件夹。",
+ "manager_root": "(同步文件夹根目录)",
+ "manager_loading": "正在加载文件…",
+ "manager_empty": "此文件夹为空。请上传文件或创建子文件夹。",
+ "upload": "上传",
+ "upload_done": "文件已上传",
+ "download_local": "下载",
+ "download_local_done": "下载已开始",
+ "delete": "删除",
+ "delete_confirm": "从同步文件夹删除“{name}”?",
+ "delete_done": "已删除"
}
}
diff --git a/meshchatx/src/frontend/public/meshchatx-docs/en/messaging.md b/meshchatx/src/frontend/public/meshchatx-docs/en/messaging.md
index 64bd5054..9a8df6c0 100644
--- a/meshchatx/src/frontend/public/meshchatx-docs/en/messaging.md
+++ b/meshchatx/src/frontend/public/meshchatx-docs/en/messaging.md
@@ -105,6 +105,7 @@ WebSocket lxmf_message event on recipient UI
- Enable **auto-announce** so your `lxmf.delivery` aspect stays visible on the mesh.
- Check **Interfaces** if messages stall. No path to the peer means LXMF cannot deliver.
- Review stamp settings before joining busy public meshes.
+- While a large message is downloading, the header shows **Cancel incoming**. That stops active LXMF delivery resource transfers (`cancel_all_inbound` / per-resource cancel). Outbound send cancel stays on each message menu.
## See also
diff --git a/package.json b/package.json
index 81b164ff..1487d4ee 100644
--- a/package.json
+++ b/package.json
@@ -69,37 +69,37 @@
"packageManager": "pnpm@11.1.2",
"devDependencies": {
"@electron/fuses": "^1.8.0",
- "@eslint/js": "^9.39.4",
+ "@eslint/js": "^9.39.5",
"@playwright/test": "^1.61.1",
- "@tailwindcss/vite": "^4.2.4",
- "@vitejs/plugin-vue": "^6.0.7",
- "@vitest/coverage-v8": "^4.1.5",
- "@vitest/ui": "^4.1.9",
+ "@tailwindcss/vite": "^4.3.3",
+ "@vitejs/plugin-vue": "^6.0.8",
+ "@vitest/coverage-v8": "^4.1.10",
+ "@vitest/ui": "^4.1.10",
"@vue/test-utils": "^2.4.11",
"cross-env": "^10.1.0",
"dayjs": "^1.11.21",
"electron": "42.4.0",
"electron-builder": "^26.15.3",
"electron-builder-squirrel-windows": "^26.15.3",
- "eslint": "^9.39.4",
+ "eslint": "^9.39.5",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-security": "^3.0.1",
- "eslint-plugin-vue": "^10.9.2",
+ "eslint-plugin-vue": "^10.10.0",
"fake-indexeddb": "^6.2.5",
- "globals": "^17.6.0",
+ "globals": "^17.7.0",
"jsdom": "^29.1.1",
- "knip": "^6.24.0",
+ "knip": "^6.29.0",
"micron-parser": "github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098",
- "prettier": "^3.9.3",
- "tailwindcss": "^4.3.0",
- "terser": "^5.48.0",
+ "prettier": "^3.9.6",
+ "tailwindcss": "^4.3.3",
+ "terser": "^5.49.0",
"typescript": "^6.0.3",
- "vite": "^8.0.16",
+ "vite": "^8.1.5",
"vite-plugin-vuetify": "^2.1.3",
- "vitest": "^4.1.5",
+ "vitest": "^4.1.10",
"vue-eslint-parser": "^10.4.1",
- "vue-tsc": "^3.3.6"
+ "vue-tsc": "^3.3.7"
},
"build": {
"appId": "com.quad4.meshchatx",
@@ -285,27 +285,27 @@
}
},
"dependencies": {
- "@fontsource/noto-sans": "^5.2.10",
+ "@fontsource/noto-sans": "^5.3.0",
"@mdi/font": "^7.4.47",
"@mdi/js": "^7.4.47",
"@tailwindcss/forms": "^0.5.11",
- "@tanstack/vue-virtual": "^3.13.30",
+ "@tanstack/vue-virtual": "^3.13.34",
"compressorjs": "^1.3.0",
- "dompurify": ">=3.4.11",
+ "dompurify": "^3.4.12",
"electron-prompt": "^1.7.0",
"emoji-picker-element": "^1.29.1",
"emoji-picker-element-data": "^1.8.0",
"jsqr": "^1.4.0",
"jszip": "^3.10.1",
- "marked": "^18.0.5",
+ "marked": "^18.0.7",
"ol": "^10.9.0",
"ol-mapbox-style": "^13.4.1",
"qrcode": "^1.5.4",
"vis-data": "^7.1.10",
"vis-network": "^9.1.13",
- "vue": "^3.5.39",
- "vue-i18n": "^11.4.6",
+ "vue": "^3.5.40",
+ "vue-i18n": "^11.4.7",
"vue-router": "^4.6.4",
- "vuetify": "^3.12.8"
+ "vuetify": "^3.12.10"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 94dea4cb..0ad843a9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -44,7 +44,7 @@ overrides:
flatted: '>=3.4.2'
tar: '>=7.5.16'
tmp: '>=0.2.7'
- undici: '>=7.28.0'
+ undici: 7.28.0
webpack: '>=5.104.0'
y18n: '>=5.0.5'
yargs-parser: '>=18.1.1'
@@ -60,8 +60,8 @@ importers:
.:
dependencies:
'@fontsource/noto-sans':
- specifier: ^5.2.10
- version: 5.2.10
+ specifier: ^5.3.0
+ version: 5.3.0
'@mdi/font':
specifier: ^7.4.47
version: 7.4.47
@@ -70,16 +70,16 @@ importers:
version: 7.4.47
'@tailwindcss/forms':
specifier: ^0.5.11
- version: 0.5.11(tailwindcss@4.3.0)
+ version: 0.5.11(tailwindcss@4.3.3)
'@tanstack/vue-virtual':
- specifier: ^3.13.30
- version: 3.13.30(vue@3.5.39(typescript@6.0.3))
+ specifier: ^3.13.34
+ version: 3.13.34(vue@3.5.40(typescript@6.0.3))
compressorjs:
specifier: ^1.3.0
version: 1.3.0
dompurify:
specifier: '>=3.4.11'
- version: 3.4.11
+ version: 3.4.12
electron-prompt:
specifier: ^1.7.0
version: 1.7.0
@@ -96,8 +96,8 @@ importers:
specifier: ^3.10.1
version: 3.10.1
marked:
- specifier: ^18.0.5
- version: 18.0.5
+ specifier: ^18.0.7
+ version: 18.0.7
ol:
specifier: ^10.9.0
version: 10.9.0(patch_hash=c7d2f18804523893b6652fabb2e43ca5120d818dbfc92b710a3f5ab3745d4ef4)
@@ -114,42 +114,42 @@ importers:
specifier: ^9.1.13
version: 9.1.13(@egjs/hammerjs@2.0.17)(component-emitter@2.0.0)(keycharm@0.4.0)(uuid@14.0.1)(vis-data@7.1.10(uuid@14.0.1)(vis-util@5.0.7(@egjs/hammerjs@2.0.17)(component-emitter@2.0.0)))(vis-util@5.0.7(@egjs/hammerjs@2.0.17)(component-emitter@2.0.0))
vue:
- specifier: ^3.5.39
- version: 3.5.39(typescript@6.0.3)
+ specifier: ^3.5.40
+ version: 3.5.40(typescript@6.0.3)
vue-i18n:
- specifier: ^11.4.6
- version: 11.4.6(vue@3.5.39(typescript@6.0.3))
+ specifier: ^11.4.7
+ version: 11.4.7(vue@3.5.40(typescript@6.0.3))
vue-router:
specifier: ^4.6.4
- version: 4.6.4(vue@3.5.39(typescript@6.0.3))
+ version: 4.6.4(vue@3.5.40(typescript@6.0.3))
vuetify:
- specifier: ^3.12.8
- version: 3.12.8(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.39(typescript@6.0.3))
+ specifier: ^3.12.10
+ version: 3.12.10(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.40(typescript@6.0.3))
devDependencies:
'@electron/fuses':
specifier: ^1.8.0
version: 1.8.0
'@eslint/js':
- specifier: ^9.39.4
- version: 9.39.4
+ specifier: ^9.39.5
+ version: 9.39.5
'@playwright/test':
specifier: ^1.61.1
version: 1.61.1
'@tailwindcss/vite':
- specifier: ^4.2.4
- version: 4.2.4(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))
+ specifier: ^4.3.3
+ version: 4.3.3(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))
'@vitejs/plugin-vue':
- specifier: ^6.0.7
- version: 6.0.7(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
+ specifier: ^6.0.8
+ version: 6.0.8(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3))
'@vitest/coverage-v8':
- specifier: ^4.1.5
- version: 4.1.5(vitest@4.1.5)
+ specifier: ^4.1.10
+ version: 4.1.10(vitest@4.1.10)
'@vitest/ui':
- specifier: ^4.1.9
- version: 4.1.9(vitest@4.1.5)
+ specifier: ^4.1.10
+ version: 4.1.10(vitest@4.1.10)
'@vue/test-utils':
specifier: ^2.4.11
- version: 2.4.11(@vue/compiler-dom@3.5.39)(@vue/server-renderer@3.5.39(vue@3.5.39(typescript@6.0.3)))(vue@3.5.39(typescript@6.0.3))
+ version: 2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.40(typescript@6.0.3))
cross-env:
specifier: ^10.1.0
version: 10.1.0
@@ -166,69 +166,65 @@ importers:
specifier: ^26.15.3
version: 26.15.3(dmg-builder@26.15.3)
eslint:
- specifier: ^9.39.4
- version: 9.39.4(jiti@2.7.0)
+ specifier: ^9.39.5
+ version: 9.39.5(jiti@2.7.0)
eslint-config-prettier:
specifier: ^10.1.8
- version: 10.1.8(eslint@9.39.4(jiti@2.7.0))
+ version: 10.1.8(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-prettier:
specifier: ^5.5.6
- version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.9.3)
+ version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6)
eslint-plugin-security:
specifier: ^3.0.1
version: 3.0.1
eslint-plugin-vue:
- specifier: ^10.9.2
- version: 10.9.2(eslint@9.39.4(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0)))
+ specifier: ^10.10.0
+ version: 10.10.0(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)))
fake-indexeddb:
specifier: ^6.2.5
version: 6.2.5
globals:
- specifier: ^17.6.0
- version: 17.6.0
+ specifier: ^17.7.0
+ version: 17.7.0
jsdom:
specifier: ^29.1.1
version: 29.1.1(@noble/hashes@2.2.0)
knip:
- specifier: ^6.24.0
- version: 6.24.0
+ specifier: ^6.29.0
+ version: 6.29.0
micron-parser:
specifier: github:RFnexus/micron-parser-js#33feb1054c8b2cb3f5f05abbb8903360d3f0c098
version: https://codeload.github.com/RFnexus/micron-parser-js/tar.gz/33feb1054c8b2cb3f5f05abbb8903360d3f0c098
prettier:
- specifier: ^3.9.3
- version: 3.9.3
+ specifier: ^3.9.6
+ version: 3.9.6
tailwindcss:
- specifier: ^4.3.0
- version: 4.3.0
+ specifier: ^4.3.3
+ version: 4.3.3
terser:
- specifier: ^5.48.0
- version: 5.48.0
+ specifier: ^5.49.0
+ version: 5.49.0
typescript:
specifier: ^6.0.3
version: 6.0.3
vite:
- specifier: ^8.0.16
- version: 8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)
+ specifier: ^8.1.5
+ version: 8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)
vite-plugin-vuetify:
specifier: ^2.1.3
- version: 2.1.3(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(vuetify@3.12.8)
+ version: 2.1.3(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3))(vuetify@3.12.10)
vitest:
- specifier: ^4.1.5
- version: 4.1.5(@types/node@24.9.0)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.9)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))
+ specifier: ^4.1.10
+ version: 4.1.10(@types/node@24.9.0)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))
vue-eslint-parser:
specifier: ^10.4.1
- version: 10.4.1(eslint@9.39.4(jiti@2.7.0))
+ version: 10.4.1(eslint@9.39.5(jiti@2.7.0))
vue-tsc:
- specifier: ^3.3.6
- version: 3.3.6(typescript@6.0.3)
+ specifier: ^3.3.7
+ version: 3.3.7(typescript@6.0.3)
packages:
- '@aashutoshrathi/word-wrap@1.2.6':
- resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
- engines: {node: '>=0.10.0'}
-
'@asamuzakjp/css-color@5.1.11':
resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
@@ -310,8 +306,8 @@ packages:
peerDependencies:
'@csstools/css-tokenizer': ^4.0.0
- '@csstools/css-syntax-patches-for-csstree@1.1.6':
- resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==}
+ '@csstools/css-syntax-patches-for-csstree@1.1.3':
+ resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==}
peerDependencies:
css-tree: ^3.2.1
peerDependenciesMeta:
@@ -326,8 +322,8 @@ packages:
resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==}
engines: {node: '>=0.8.0'}
- '@electron-internal/extract-zip@1.0.4':
- resolution: {integrity: sha512-Zr1Vs7E9tpCNhZHDAbFVXc2gEVCG9RqPDjrno5+bdgB6LRAuvgyMHJut4NCVyYwtAieapMzc3fiQ3CSTi75ARg==}
+ '@electron-internal/extract-zip@1.0.1':
+ resolution: {integrity: sha512-rdlxalBCtnzr9+MFptygKrrEB5rmCv0rKAAAfuaqEWVu4m5ZIxVZeh5b1zPZy2/mLvAL3VHlo+xSaC5ZUSZzeg==}
engines: {node: '>=22.12.0'}
'@electron/asar@3.4.1':
@@ -352,8 +348,8 @@ packages:
engines: {node: '>=12.0.0'}
hasBin: true
- '@electron/rebuild@4.0.6':
- resolution: {integrity: sha512-323ygp5Lz4DP2nVu54NLCx4n0TKsQ3OMDG3PPeOFjOTMDtSxGbCi7mQfWBc5C80pp+9awEsiHx9HR9DfAM/IEQ==}
+ '@electron/rebuild@4.0.4':
+ resolution: {integrity: sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg==}
engines: {node: '>=22.12.0'}
hasBin: true
@@ -366,26 +362,17 @@ packages:
engines: {node: '>=14.14'}
hasBin: true
- '@emnapi/core@1.10.0':
- resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
-
- '@emnapi/core@1.11.0':
- resolution: {integrity: sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==}
-
'@emnapi/core@1.11.1':
resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==}
- '@emnapi/runtime@1.10.0':
- resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
-
- '@emnapi/runtime@1.11.0':
- resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==}
+ '@emnapi/core@1.11.2':
+ resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==}
'@emnapi/runtime@1.11.1':
resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==}
- '@emnapi/wasi-threads@1.2.1':
- resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+ '@emnapi/runtime@1.11.2':
+ resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==}
'@emnapi/wasi-threads@1.2.2':
resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==}
@@ -393,8 +380,8 @@ packages:
'@epic-web/invariant@1.0.0':
resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==}
- '@eslint-community/eslint-utils@4.4.0':
- resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
+ '@eslint-community/eslint-utils@4.10.1':
+ resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
@@ -405,8 +392,8 @@ packages:
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
- '@eslint-community/regexpp@4.12.1':
- resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint/config-array@0.21.2':
@@ -421,12 +408,12 @@ packages:
resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/eslintrc@3.3.5':
- resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==}
+ '@eslint/eslintrc@3.3.6':
+ resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@9.39.4':
- resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==}
+ '@eslint/js@9.39.5':
+ resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@eslint/object-schema@2.1.7':
@@ -446,53 +433,53 @@ packages:
'@noble/hashes':
optional: true
- '@fontsource/noto-sans@5.2.10':
- resolution: {integrity: sha512-J58RVfS/C0Z2VBF+PoU260Tx8cdRGYuS+e3yQe4hYaIYDl0sEVn5CzlLo5zVRvQD0HaIUTV8AZMfqR7rtdEpqQ==}
+ '@fontsource/noto-sans@5.3.0':
+ resolution: {integrity: sha512-fBCog2PY7DiVVTEEqtI/Qdinx/knobHYfoGpjFXdfBX5RoJaBp1Prw2G75p0OIsdlMZg3cLo0c+YbIUYOxnQJw==}
+
+ '@gar/promise-retry@1.0.3':
+ resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
+ '@humanfs/core@0.19.2':
+ resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
+ engines: {node: '>=18.18.0'}
- '@humanfs/core@0.19.1':
- resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
+ '@humanfs/node@0.16.8':
+ resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
engines: {node: '>=18.18.0'}
- '@humanfs/node@0.16.6':
- resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==}
+ '@humanfs/types@0.15.0':
+ resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
engines: {node: '>=18.18.0'}
'@humanwhocodes/module-importer@1.0.1':
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
- '@humanwhocodes/retry@0.3.0':
- resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
- engines: {node: '>=18.18'}
-
- '@humanwhocodes/retry@0.4.2':
- resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
engines: {node: '>=18.18'}
- '@intlify/core-base@11.4.6':
- resolution: {integrity: sha512-EOeHO95XESK9IFHgHeZXunsM/WBAoCA0DlaWODvx14vKmetAuS97t+l6Xe9hTUqntPpF93vtVSjjUDafw3wXMw==}
+ '@intlify/core-base@11.4.7':
+ resolution: {integrity: sha512-MSB/sBKwEWJTILvQIhg2rnIcwPpLayo3wGwvVA+dJTNeUBD9GoqQgAaSOLdI9iOPDHCm9YoVnLqpfzza98MpkQ==}
engines: {node: '>= 22'}
- '@intlify/devtools-types@11.4.6':
- resolution: {integrity: sha512-wowQPpNem56b2d43IJmqbrzG2FeBKe5f/kUGlpNuBmXs6OSqncF8m1+1lxHuW8ISZJF0ma2RkW3iLkw0g0G4VA==}
+ '@intlify/devtools-types@11.4.7':
+ resolution: {integrity: sha512-GSz+J+hqH+AEpAHIYya6fSufS30OaMnG39HiZX7DmGKi3+aaLvassCfsXENEc4Wr4m68q2YP0QdMdB3D9UeAXg==}
engines: {node: '>= 22'}
- '@intlify/message-compiler@11.4.6':
- resolution: {integrity: sha512-5nj3jULqeTAC1WovwMs1LQWgatTa2pM/rXN9T3XW8rdOtXW9ZF6/GLSNFTKDQmPLwclhPdgUWLJ/4w3fMeeC/Q==}
+ '@intlify/message-compiler@11.4.7':
+ resolution: {integrity: sha512-bHxmh7n94N4N1evADeb7XTkc3jTw6Ki5biMFZVSX6Jmk+iehy8/maeH2XUsBI27rtKIK+Hzc6QnVAKggUwylKw==}
engines: {node: '>= 22'}
- '@intlify/shared@11.4.6':
- resolution: {integrity: sha512-m1p1HHAMLhqSpTRH7VnXdrN0CQ4y+9vunFkpLkbD8soIuBsnQdawZXqMCgvwI2UVF9Ww7sVaw7g9tV2VO7shoA==}
+ '@intlify/shared@11.4.7':
+ resolution: {integrity: sha512-OtjPZan3No2OZZFnMUiCVsXC6+j+XRwEywaFDk0AoayAbLuPesyDloXhJZLl9JUl5vHZeQUkYSbEA8VX+CWMjg==}
engines: {node: '>= 22'}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
- '@isaacs/cliui@9.0.0':
- resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==}
- engines: {node: '>=18'}
-
'@isaacs/fs-minipass@4.0.0':
resolution: {integrity: sha512-S00nN1Qt3z3dSP6Db45fj/mksrAq5XWNIJ/SWXGP8XPT2jrzEuYRCSEx08JpJwBcG2F1xgiOtBMGDU0AZHmxew==}
engines: {node: '>=18.0.0'}
@@ -501,9 +488,8 @@ packages:
resolution: {integrity: sha512-bFDbLEZ84DtNwXILbufXeQMz3tcGSfy+ROgvnB9jUm+t48G4Po+1UhhyFh6GIsFOE2R31Ab0ddeMv+Z9gcYC4g==}
engines: {node: '>=6.0.0'}
- '@jridgewell/gen-mapping@0.3.5':
- resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
- engines: {node: '>=6.0.0'}
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
'@jridgewell/remapping@2.3.5':
resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
@@ -532,18 +518,12 @@ packages:
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
- '@jridgewell/trace-mapping@0.3.24':
- resolution: {integrity: sha512-+VaWXDa6+l6MhflBvVXjIEAzb59nQ2JUK3bwRp2zRpPtU+8TFRy9Gg/5oIcNlkEL5PGlBFGfemUVvIgLnTzq7Q==}
-
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
- '@keyv/serialize@1.1.1':
- resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
-
'@malept/cross-spawn-promise@2.0.0':
resolution: {integrity: sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==}
engines: {node: '>= 12.13.0'}
@@ -552,15 +532,15 @@ packages:
resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==}
engines: {node: '>= 10.0.0'}
- '@mapbox/jsonlint-lines-primitives@2.0.3':
- resolution: {integrity: sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==}
- engines: {node: '>= 22'}
+ '@mapbox/jsonlint-lines-primitives@2.0.2':
+ resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==}
+ engines: {node: '>= 0.6'}
- '@mapbox/unitbezier@1.0.0':
- resolution: {integrity: sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==}
+ '@mapbox/unitbezier@0.0.1':
+ resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==}
- '@maplibre/maplibre-gl-style-spec@24.10.0':
- resolution: {integrity: sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==}
+ '@maplibre/maplibre-gl-style-spec@24.4.1':
+ resolution: {integrity: sha512-UKhA4qv1h30XT768ccSv5NjNCX+dgfoq2qlLVmKejspPcSQTYD4SrVucgqegmYcKcmwf06wcNAa/kRd0NHWbUg==}
hasBin: true
'@mdi/font@7.4.47':
@@ -569,14 +549,8 @@ packages:
'@mdi/js@7.4.47':
resolution: {integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==}
- '@napi-rs/wasm-runtime@1.1.4':
- resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==}
- peerDependencies:
- '@emnapi/core': ^1.7.1
- '@emnapi/runtime': ^1.7.1
-
- '@napi-rs/wasm-runtime@1.1.5':
- resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==}
+ '@napi-rs/wasm-runtime@1.1.6':
+ resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==}
peerDependencies:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
@@ -589,247 +563,259 @@ packages:
resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==}
engines: {node: '>= 20.19.0'}
+ '@npmcli/agent@4.0.2':
+ resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
+ '@npmcli/fs@5.0.0':
+ resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
+ '@npmcli/redact@4.0.0':
+ resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
'@one-ini/wasm@0.1.1':
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
- '@oxc-parser/binding-android-arm-eabi@0.137.0':
- resolution: {integrity: sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==}
+ '@oxc-parser/binding-android-arm-eabi@0.140.0':
+ resolution: {integrity: sha512-ZfjDZ422mo7eo3b3VltqNsV9kmv1qt/sPEAMSl64iOSwhVfd0eIZ9LB79Mbs1xYXJnk7WSROwzBCKDIiVxPTvQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
- '@oxc-parser/binding-android-arm64@0.137.0':
- resolution: {integrity: sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==}
+ '@oxc-parser/binding-android-arm64@0.140.0':
+ resolution: {integrity: sha512-Ia8jSvikUX6Sf+Ht+KOCUF/k1HpR0VlmqIYymubmWDebOEGtsyliHDR6JxsZ4IX3/c/GbrB1uh09aVGQv/LQmQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
- '@oxc-parser/binding-darwin-arm64@0.137.0':
- resolution: {integrity: sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==}
+ '@oxc-parser/binding-darwin-arm64@0.140.0':
+ resolution: {integrity: sha512-G6VK0nK61pH0d0mBjUqSZbVxGqqO5uzeginLDQj+gOO6ObfJjXRwgkD/ol0w1INcnFeAb6YGGO7qc3ueGHaycQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
- '@oxc-parser/binding-darwin-x64@0.137.0':
- resolution: {integrity: sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==}
+ '@oxc-parser/binding-darwin-x64@0.140.0':
+ resolution: {integrity: sha512-HazBOuZzd2pO1C2uMmp8Gv7mhzMHqKSKDS1OZfcLEvpIcgA+48J92HEtNanVHDIzRD9PRPCV6aS6fkZIWOVl8Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@oxc-parser/binding-freebsd-x64@0.137.0':
- resolution: {integrity: sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==}
+ '@oxc-parser/binding-freebsd-x64@0.140.0':
+ resolution: {integrity: sha512-9hSUU+HmTUyOe4JzMHxNGgLWNY7rrO+6ShicZwImNJacEAACDMIkuEQQkvXSL+WJN50jaNtLYJv8s4OcBdpyUQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
- '@oxc-parser/binding-linux-arm-gnueabihf@0.137.0':
- resolution: {integrity: sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==}
+ '@oxc-parser/binding-linux-arm-gnueabihf@0.140.0':
+ resolution: {integrity: sha512-RAEuQsYtS0KcDFqN0ABTjyyNlokS91JeuDuoW9tEbG0JTbRNXnpQUdbYc/16JoA6Z/2ALbNrE3KmxtqDiuIjCQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@oxc-parser/binding-linux-arm-musleabihf@0.137.0':
- resolution: {integrity: sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==}
+ '@oxc-parser/binding-linux-arm-musleabihf@0.140.0':
+ resolution: {integrity: sha512-c4CkHvPvqfojouredJ0w3e6+jiBq0SbFyhH61kr/zPb/7XsaYTNKQ54vmlSsopfdQbNDX40ZeK9Abs2Qet6wcw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@oxc-parser/binding-linux-arm64-gnu@0.137.0':
- resolution: {integrity: sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==}
+ '@oxc-parser/binding-linux-arm64-gnu@0.140.0':
+ resolution: {integrity: sha512-yrjmLj8ixPB25yqvPGr28meGjb+keed7m1GqqY/0uqkhZIoT4t9zmfwUgFEtC33C7dtE+UQ7TU0IaVxf97SWJg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@oxc-parser/binding-linux-arm64-musl@0.137.0':
- resolution: {integrity: sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==}
+ '@oxc-parser/binding-linux-arm64-musl@0.140.0':
+ resolution: {integrity: sha512-ggGMQTN8Agwxp2WiLMpdY671dt0qTDJWiWlJeig3HnUwTnerRl0J2JdGVghWBeDcss2D9S2V2Js6dZHEiVabVA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@oxc-parser/binding-linux-ppc64-gnu@0.137.0':
- resolution: {integrity: sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==}
+ '@oxc-parser/binding-linux-ppc64-gnu@0.140.0':
+ resolution: {integrity: sha512-IgTs8xYAFgAUGNmR65tIqjlJ8vKgrfXzC515e9goSdfMyKQV4aJpd2pUUudU4u51G64H0/DSEJEXKOraxm9ZCA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@oxc-parser/binding-linux-riscv64-gnu@0.137.0':
- resolution: {integrity: sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==}
+ '@oxc-parser/binding-linux-riscv64-gnu@0.140.0':
+ resolution: {integrity: sha512-A1x+PMWZmSGaFVOx2YeNTFau8uD+QO14/vLP4GrcuvUPs3+nBkUOjy9Lus86ftHsDojjYMbvBelmKc3F7Rv08g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
libc: [glibc]
- '@oxc-parser/binding-linux-riscv64-musl@0.137.0':
- resolution: {integrity: sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==}
+ '@oxc-parser/binding-linux-riscv64-musl@0.140.0':
+ resolution: {integrity: sha512-zBqpfRo2myWPrPo5xUjeZqlnPXPXsX8BcWtWff66/eGRQdbPjhzPgXa/F+AtxT2afUViPxbuDlwscMKzQ5tg+g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
libc: [musl]
- '@oxc-parser/binding-linux-s390x-gnu@0.137.0':
- resolution: {integrity: sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==}
+ '@oxc-parser/binding-linux-s390x-gnu@0.140.0':
+ resolution: {integrity: sha512-2M1DPm/8w9I//YzFlFC9qXw+r2tJFh5CYwRlYTq2vUJQS7qoQftEDeCZ8EnN7KHtvSiXvYj8mZI5pR7DpXmcEw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@oxc-parser/binding-linux-x64-gnu@0.137.0':
- resolution: {integrity: sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==}
+ '@oxc-parser/binding-linux-x64-gnu@0.140.0':
+ resolution: {integrity: sha512-8aRDbZ/U/jO8N7go1MO72jtbpb4uswV8d7vOkMvt/BPgZiyEYvl1VIWK4ESxZZhnJ4tqwVldgX7dNiP/eB1Jdg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@oxc-parser/binding-linux-x64-musl@0.137.0':
- resolution: {integrity: sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==}
+ '@oxc-parser/binding-linux-x64-musl@0.140.0':
+ resolution: {integrity: sha512-xRqpeI8U2sQQS1W5BMWRyMTxtagkuLG2dEWruet5lFsWHTvBth11/TpSaJatHdqVVwHN0q3uuoS9zRsGinq8hg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
- '@oxc-parser/binding-openharmony-arm64@0.137.0':
- resolution: {integrity: sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==}
+ '@oxc-parser/binding-openharmony-arm64@0.140.0':
+ resolution: {integrity: sha512-GbGRe26MqAKciFRvXeHNQJ6VAHYs9R4miP89sEAncysM3n+f4lnyLWgsa9kklJNpfnxdq2yRoNYHFqwBckVimw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
- '@oxc-parser/binding-wasm32-wasi@0.137.0':
- resolution: {integrity: sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==}
+ '@oxc-parser/binding-wasm32-wasi@0.140.0':
+ resolution: {integrity: sha512-vFiC1hqys+hkX1GnQkIoiTQJNiUm43Z0lO35ETKXTw0YtpW7+cN58YRRXFAQQ+TgpkIi3lrhcxdlnqz+Oi3ptQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [wasm32]
- '@oxc-parser/binding-win32-arm64-msvc@0.137.0':
- resolution: {integrity: sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==}
+ '@oxc-parser/binding-win32-arm64-msvc@0.140.0':
+ resolution: {integrity: sha512-fGSQldwEYKhM+H8uLt76Op8hh5+FYaR6lvvQ1Txw3Mhn86DyQXLcI0fi1EkFlTK7F+46OCk/j0AJMzZQm6g5Xg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
- '@oxc-parser/binding-win32-ia32-msvc@0.137.0':
- resolution: {integrity: sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==}
+ '@oxc-parser/binding-win32-ia32-msvc@0.140.0':
+ resolution: {integrity: sha512-sDS2Bai+g3ZWYwfZqmosiSuFDBcVnZ3Ta6pszzsiJoLMqsJEWKcxXXbGa7b7yXr++W2lQNPb3ZRJ8czseqL7RA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ia32]
os: [win32]
- '@oxc-parser/binding-win32-x64-msvc@0.137.0':
- resolution: {integrity: sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==}
+ '@oxc-parser/binding-win32-x64-msvc@0.140.0':
+ resolution: {integrity: sha512-kHbE1zWyb5OQgJA6/5P4WjiuB01sYdQwtZnSSyE58FQEXDAMnyeeq4vj7KgN75i5SlBzOs8A5MrtlD3gOlDKqQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
- '@oxc-project/types@0.133.0':
- resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
+ '@oxc-project/types@0.139.0':
+ resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==}
- '@oxc-project/types@0.137.0':
- resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==}
+ '@oxc-project/types@0.140.0':
+ resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==}
- '@oxc-resolver/binding-android-arm-eabi@11.21.3':
- resolution: {integrity: sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==}
+ '@oxc-resolver/binding-android-arm-eabi@11.24.2':
+ resolution: {integrity: sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==}
cpu: [arm]
os: [android]
- '@oxc-resolver/binding-android-arm64@11.21.3':
- resolution: {integrity: sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==}
+ '@oxc-resolver/binding-android-arm64@11.24.2':
+ resolution: {integrity: sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==}
cpu: [arm64]
os: [android]
- '@oxc-resolver/binding-darwin-arm64@11.21.3':
- resolution: {integrity: sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==}
+ '@oxc-resolver/binding-darwin-arm64@11.24.2':
+ resolution: {integrity: sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==}
cpu: [arm64]
os: [darwin]
- '@oxc-resolver/binding-darwin-x64@11.21.3':
- resolution: {integrity: sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==}
+ '@oxc-resolver/binding-darwin-x64@11.24.2':
+ resolution: {integrity: sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==}
cpu: [x64]
os: [darwin]
- '@oxc-resolver/binding-freebsd-x64@11.21.3':
- resolution: {integrity: sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==}
+ '@oxc-resolver/binding-freebsd-x64@11.24.2':
+ resolution: {integrity: sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==}
cpu: [x64]
os: [freebsd]
- '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.3':
- resolution: {integrity: sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==}
+ '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2':
+ resolution: {integrity: sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==}
cpu: [arm]
os: [linux]
- '@oxc-resolver/binding-linux-arm-musleabihf@11.21.3':
- resolution: {integrity: sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==}
+ '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2':
+ resolution: {integrity: sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==}
cpu: [arm]
os: [linux]
- '@oxc-resolver/binding-linux-arm64-gnu@11.21.3':
- resolution: {integrity: sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==}
+ '@oxc-resolver/binding-linux-arm64-gnu@11.24.2':
+ resolution: {integrity: sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@oxc-resolver/binding-linux-arm64-musl@11.21.3':
- resolution: {integrity: sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==}
+ '@oxc-resolver/binding-linux-arm64-musl@11.24.2':
+ resolution: {integrity: sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@oxc-resolver/binding-linux-ppc64-gnu@11.21.3':
- resolution: {integrity: sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==}
+ '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2':
+ resolution: {integrity: sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@oxc-resolver/binding-linux-riscv64-gnu@11.21.3':
- resolution: {integrity: sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==}
+ '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2':
+ resolution: {integrity: sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
- '@oxc-resolver/binding-linux-riscv64-musl@11.21.3':
- resolution: {integrity: sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==}
+ '@oxc-resolver/binding-linux-riscv64-musl@11.24.2':
+ resolution: {integrity: sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==}
cpu: [riscv64]
os: [linux]
libc: [musl]
- '@oxc-resolver/binding-linux-s390x-gnu@11.21.3':
- resolution: {integrity: sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==}
+ '@oxc-resolver/binding-linux-s390x-gnu@11.24.2':
+ resolution: {integrity: sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@oxc-resolver/binding-linux-x64-gnu@11.21.3':
- resolution: {integrity: sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==}
+ '@oxc-resolver/binding-linux-x64-gnu@11.24.2':
+ resolution: {integrity: sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@oxc-resolver/binding-linux-x64-musl@11.21.3':
- resolution: {integrity: sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==}
+ '@oxc-resolver/binding-linux-x64-musl@11.24.2':
+ resolution: {integrity: sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==}
cpu: [x64]
os: [linux]
libc: [musl]
- '@oxc-resolver/binding-openharmony-arm64@11.21.3':
- resolution: {integrity: sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==}
+ '@oxc-resolver/binding-openharmony-arm64@11.24.2':
+ resolution: {integrity: sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==}
cpu: [arm64]
os: [openharmony]
- '@oxc-resolver/binding-wasm32-wasi@11.21.3':
- resolution: {integrity: sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==}
+ '@oxc-resolver/binding-wasm32-wasi@11.24.2':
+ resolution: {integrity: sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
- '@oxc-resolver/binding-win32-arm64-msvc@11.21.3':
- resolution: {integrity: sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==}
+ '@oxc-resolver/binding-win32-arm64-msvc@11.24.2':
+ resolution: {integrity: sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==}
cpu: [arm64]
os: [win32]
- '@oxc-resolver/binding-win32-x64-msvc@11.21.3':
- resolution: {integrity: sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==}
+ '@oxc-resolver/binding-win32-x64-msvc@11.24.2':
+ resolution: {integrity: sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==}
cpu: [x64]
os: [win32]
- '@peculiar/asn1-schema@2.8.0':
- resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==}
+ '@peculiar/asn1-schema@2.7.0':
+ resolution: {integrity: sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg==}
'@peculiar/json-schema@1.1.12':
resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==}
@@ -861,97 +847,97 @@ packages:
'@polka/url@1.0.0-next.24':
resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==}
- '@rolldown/binding-android-arm64@1.0.3':
- resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==}
+ '@rolldown/binding-android-arm64@1.1.5':
+ resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
- '@rolldown/binding-darwin-arm64@1.0.3':
- resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==}
+ '@rolldown/binding-darwin-arm64@1.1.5':
+ resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
- '@rolldown/binding-darwin-x64@1.0.3':
- resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==}
+ '@rolldown/binding-darwin-x64@1.1.5':
+ resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@rolldown/binding-freebsd-x64@1.0.3':
- resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==}
+ '@rolldown/binding-freebsd-x64@1.1.5':
+ resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
- '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
- resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==}
+ '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
+ resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@rolldown/binding-linux-arm64-gnu@1.0.3':
- resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==}
+ '@rolldown/binding-linux-arm64-gnu@1.1.5':
+ resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-arm64-musl@1.0.3':
- resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==}
+ '@rolldown/binding-linux-arm64-musl@1.1.5':
+ resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@rolldown/binding-linux-ppc64-gnu@1.0.3':
- resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==}
+ '@rolldown/binding-linux-ppc64-gnu@1.1.5':
+ resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-s390x-gnu@1.0.3':
- resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==}
+ '@rolldown/binding-linux-s390x-gnu@1.1.5':
+ resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-x64-gnu@1.0.3':
- resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==}
+ '@rolldown/binding-linux-x64-gnu@1.1.5':
+ resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-x64-musl@1.0.3':
- resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==}
+ '@rolldown/binding-linux-x64-musl@1.1.5':
+ resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
- '@rolldown/binding-openharmony-arm64@1.0.3':
- resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==}
+ '@rolldown/binding-openharmony-arm64@1.1.5':
+ resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
- '@rolldown/binding-wasm32-wasi@1.0.3':
- resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==}
+ '@rolldown/binding-wasm32-wasi@1.1.5':
+ resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [wasm32]
- '@rolldown/binding-win32-arm64-msvc@1.0.3':
- resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==}
+ '@rolldown/binding-win32-arm64-msvc@1.1.5':
+ resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
- '@rolldown/binding-win32-x64-msvc@1.0.3':
- resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==}
+ '@rolldown/binding-win32-x64-msvc@1.1.5':
+ resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
@@ -962,84 +948,85 @@ packages:
'@rolldown/pluginutils@1.0.1':
resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==}
- '@sec-ant/readable-stream@0.4.1':
- resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
-
- '@sindresorhus/is@8.1.0':
- resolution: {integrity: sha512-2SX/1jW6CIMAiebvVv5ZInoCEuWQmMyBoJXXGC6Vjakjp/fpxP5eHs7/V6WKuPEIbuK06+VpjH+vjLQhr98rDQ==}
- engines: {node: '>=22'}
+ '@sindresorhus/is@4.0.0':
+ resolution: {integrity: sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ==}
+ engines: {node: '>=10'}
'@standard-schema/spec@1.1.0':
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+ '@szmarczak/http-timer@4.0.5':
+ resolution: {integrity: sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==}
+ engines: {node: '>=10'}
+
'@tailwindcss/forms@0.5.11':
resolution: {integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==}
peerDependencies:
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1'
- '@tailwindcss/node@4.2.4':
- resolution: {integrity: sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==}
+ '@tailwindcss/node@4.3.3':
+ resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==}
- '@tailwindcss/oxide-android-arm64@4.2.4':
- resolution: {integrity: sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==}
+ '@tailwindcss/oxide-android-arm64@4.3.3':
+ resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [android]
- '@tailwindcss/oxide-darwin-arm64@4.2.4':
- resolution: {integrity: sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==}
+ '@tailwindcss/oxide-darwin-arm64@4.3.3':
+ resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [darwin]
- '@tailwindcss/oxide-darwin-x64@4.2.4':
- resolution: {integrity: sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==}
+ '@tailwindcss/oxide-darwin-x64@4.3.3':
+ resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==}
engines: {node: '>= 20'}
cpu: [x64]
os: [darwin]
- '@tailwindcss/oxide-freebsd-x64@4.2.4':
- resolution: {integrity: sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==}
+ '@tailwindcss/oxide-freebsd-x64@4.3.3':
+ resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==}
engines: {node: '>= 20'}
cpu: [x64]
os: [freebsd]
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4':
- resolution: {integrity: sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==}
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3':
+ resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==}
engines: {node: '>= 20'}
cpu: [arm]
os: [linux]
- '@tailwindcss/oxide-linux-arm64-gnu@4.2.4':
- resolution: {integrity: sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==}
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.3':
+ resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@tailwindcss/oxide-linux-arm64-musl@4.2.4':
- resolution: {integrity: sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==}
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.3':
+ resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@tailwindcss/oxide-linux-x64-gnu@4.2.4':
- resolution: {integrity: sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==}
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.3':
+ resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@tailwindcss/oxide-linux-x64-musl@4.2.4':
- resolution: {integrity: sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==}
+ '@tailwindcss/oxide-linux-x64-musl@4.3.3':
+ resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==}
engines: {node: '>= 20'}
cpu: [x64]
os: [linux]
libc: [musl]
- '@tailwindcss/oxide-wasm32-wasi@4.2.4':
- resolution: {integrity: sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==}
+ '@tailwindcss/oxide-wasm32-wasi@4.3.3':
+ resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
bundledDependencies:
@@ -1050,70 +1037,70 @@ packages:
- '@emnapi/wasi-threads'
- tslib
- '@tailwindcss/oxide-win32-arm64-msvc@4.2.4':
- resolution: {integrity: sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==}
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.3':
+ resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==}
engines: {node: '>= 20'}
cpu: [arm64]
os: [win32]
- '@tailwindcss/oxide-win32-x64-msvc@4.2.4':
- resolution: {integrity: sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==}
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.3':
+ resolution: {integrity: sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==}
engines: {node: '>= 20'}
cpu: [x64]
os: [win32]
- '@tailwindcss/oxide@4.2.4':
- resolution: {integrity: sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==}
+ '@tailwindcss/oxide@4.3.3':
+ resolution: {integrity: sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==}
engines: {node: '>= 20'}
- '@tailwindcss/vite@4.2.4':
- resolution: {integrity: sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==}
+ '@tailwindcss/vite@4.3.3':
+ resolution: {integrity: sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==}
peerDependencies:
vite: ^5.2.0 || ^6 || ^7 || ^8
- '@tanstack/virtual-core@3.17.2':
- resolution: {integrity: sha512-w43MvWvmShpb6kIC9MOoLyUkLmRTLPjt61bHWs+X29hACSpX+n8DvgZ3qM7cUfflKlRRcHR9KVJE6TmcqnQvcA==}
+ '@tanstack/virtual-core@3.17.6':
+ resolution: {integrity: sha512-h0/Ebo18CkOrChlQIhNtQkM5ySUnh/GumQ/D1st3hG2HWUPEF+ILUc2k29UtivCi/9G7w7G3/f7Xyd5cCFbKBw==}
- '@tanstack/vue-virtual@3.13.30':
- resolution: {integrity: sha512-5IpTZf5US81z9CHaRm2imVC44WDU1V/pd8mN1OIvIerRmo6C179UN+vnzlO/dx9Fpt9X7Rjl7fyvolPhPgtfqg==}
+ '@tanstack/vue-virtual@3.13.34':
+ resolution: {integrity: sha512-CBqbCcnVsKpl9IJ7frPnbBmAqmc7JttSySg04kgLYJ4yYuC8JsAbtUHP0yLtWGLyByjihN3SwaDCgHQ+Z4iPoA==}
peerDependencies:
vue: ^2.7.0 || ^3.0.0
- '@tybys/wasm-util@0.10.1':
- resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+ '@tybys/wasm-util@0.10.3':
+ resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==}
- '@tybys/wasm-util@0.10.2':
- resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
+ '@types/cacheable-request@6.0.1':
+ resolution: {integrity: sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==}
- '@types/chai@5.2.2':
- resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
- '@types/debug@4.1.13':
- resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
+ '@types/debug@4.1.6':
+ resolution: {integrity: sha512-7fDOJFA/x8B+sO1901BmHlf5dE1cxBU8mRXj8QOEDnn16hhGJv/IHxJtZhvsabZsIMn0eLIyeOKAeqSNJJYTpA==}
- '@types/deep-eql@4.0.0':
- resolution: {integrity: sha512-UkfTYLA64vGUyR4ez8ybIC01wtilS/ydOqKL1fSHOvnX1sVm1sI5btr7Q+bqpdaPGD+QNhOgBXq1HGjO+99e1A==}
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
'@types/estree@1.0.0':
resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
- '@types/estree@1.0.6':
- resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
'@types/fs-extra@9.0.13':
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
- '@types/hammerjs@2.0.36':
- resolution: {integrity: sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==}
+ '@types/hammerjs@2.0.46':
+ resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==}
- '@types/http-cache-semantics@4.2.0':
- resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==}
+ '@types/http-cache-semantics@4.0.0':
+ resolution: {integrity: sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
- '@types/ms@2.1.0':
- resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+ '@types/keyv@3.0.0':
+ resolution: {integrity: sha512-WC/MXa64qaA+H2NHO+11zbGiPbqgGNliOxSkyntZL8+JGHiePkqHC31s5K1zJGLz5VlSv4M3KEUiAVP9Wyie3g==}
'@types/node@0.0.0':
resolution: {integrity: sha512-WTVTibPZsmWNm4thlkPnc0QPbT4+Vw+48e4otLxwdBVZduwrZ2pDvAH9dnpOe/fQnNl84PO4XOq3dHAyjaornA==}
@@ -1124,30 +1111,33 @@ packages:
'@types/rbush@4.0.0':
resolution: {integrity: sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ==}
+ '@types/responselike@1.0.0':
+ resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
+
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
- '@vitejs/plugin-vue@6.0.7':
- resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==}
+ '@vitejs/plugin-vue@6.0.8':
+ resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==}
engines: {node: ^20.19.0 || >=22.12.0}
peerDependencies:
vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0
vue: ^3.2.25
- '@vitest/coverage-v8@4.1.5':
- resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==}
+ '@vitest/coverage-v8@4.1.10':
+ resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==}
peerDependencies:
- '@vitest/browser': 4.1.5
- vitest: 4.1.5
+ '@vitest/browser': 4.1.10
+ vitest: 4.1.10
peerDependenciesMeta:
'@vitest/browser':
optional: true
- '@vitest/expect@4.1.5':
- resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==}
+ '@vitest/expect@4.1.10':
+ resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==}
- '@vitest/mocker@4.1.5':
- resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==}
+ '@vitest/mocker@4.1.10':
+ resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==}
peerDependencies:
msw: ^2.4.9
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -1157,31 +1147,25 @@ packages:
vite:
optional: true
- '@vitest/pretty-format@4.1.5':
- resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==}
-
- '@vitest/pretty-format@4.1.9':
- resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==}
+ '@vitest/pretty-format@4.1.10':
+ resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==}
- '@vitest/runner@4.1.5':
- resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==}
+ '@vitest/runner@4.1.10':
+ resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==}
- '@vitest/snapshot@4.1.5':
- resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==}
+ '@vitest/snapshot@4.1.10':
+ resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==}
- '@vitest/spy@4.1.5':
- resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==}
+ '@vitest/spy@4.1.10':
+ resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==}
- '@vitest/ui@4.1.9':
- resolution: {integrity: sha512-U/cRvtqfEPj27FI1n9cyUvi4vXXdcLhjJiI+InYKdk8hP4VrS6RXOjGL7rfFaeBc37iRKANsR6eEzIoC7lmgBQ==}
+ '@vitest/ui@4.1.10':
+ resolution: {integrity: sha512-EOUqfXHTXtpSHsyLHH40ts3Ue+hRhSGwzwzMlK0dTEOLSDYyOXLyr5JDGmHQWhN2DYI30gw6dVx3cdgM9FZl+Q==}
peerDependencies:
- vitest: 4.1.9
+ vitest: 4.1.10
- '@vitest/utils@4.1.5':
- resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==}
-
- '@vitest/utils@4.1.9':
- resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==}
+ '@vitest/utils@4.1.10':
+ resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==}
'@volar/language-core@2.4.28':
resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==}
@@ -1192,40 +1176,41 @@ packages:
'@volar/typescript@2.4.28':
resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==}
- '@vue/compiler-core@3.5.39':
- resolution: {integrity: sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==}
+ '@vue/compiler-core@3.5.40':
+ resolution: {integrity: sha512-39E8IgOhTbVDnoJFMKc2DvYnypcZwUqgUhQkccva/0m6FUwtIKSGV7n1hpVmYcFaoRAwf9pBcwnKlCEsN63ZEQ==}
+
+ '@vue/compiler-dom@3.5.40':
+ resolution: {integrity: sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA==}
- '@vue/compiler-dom@3.5.39':
- resolution: {integrity: sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==}
+ '@vue/compiler-sfc@3.5.40':
+ resolution: {integrity: sha512-gIf497P4kpuALcvs5n3AEg1Vdn0pSY4XbjASIfHNYF1/MP3T2Mf2STERTubysBxCRxzJGJYtF/O7vwJrxFB3Vw==}
- '@vue/compiler-sfc@3.5.39':
- resolution: {integrity: sha512-d0ki86iOyN8LoZPBmk5SJWNwHP19CnDDCfuo//+2WJa2g5Ke0Jay983PIBIcSSzldC68I8DrD5GrHV3OSDfodg==}
+ '@vue/compiler-ssr@3.5.40':
+ resolution: {integrity: sha512-rrE5xiXG663+vHCHa3J9p2z5OcBRjXmoqenprJxAFQxg5pSshzeBiCE6pu46axapRJ2Adk0YDA2BRZVjiHXnhg==}
- '@vue/compiler-ssr@3.5.39':
- resolution: {integrity: sha512-Ce7/wvwMHai74bdszfXExdazFigYnlF9zgCmEQUcM1j0fOymlouZ7XilTYNo8oUjhlnjYOZbGrcYKuqjz89Ucw==}
+ '@vue/devtools-api@6.5.0':
+ resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
'@vue/devtools-api@6.6.4':
resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
- '@vue/language-core@3.3.6':
- resolution: {integrity: sha512-LgBMZAy2sR3cQWknpyaxnI6yBkqDfLBPkbdhwRhQCvzfNJRQXPilgQIrdI/v4ytJ0sAq9bWhaPsjqBqneomJ3Q==}
+ '@vue/language-core@3.3.7':
+ resolution: {integrity: sha512-LzmkKinXAMMoh8Jfi/jMUSDUjuPdv8mynH5WJGKfXyZtDw3hQ6GBaoI6Bcnl/Xqlu32q/0Z6i/trp4VXykzyLw==}
- '@vue/reactivity@3.5.39':
- resolution: {integrity: sha512-TpsuBJ9gGlZa5d23XcM2y8EXanz9dZeVDQBXRwzy46ItgvM+rWpzs+UVM0wcRLxGvcav0HE5jz2gNL53xlRAog==}
+ '@vue/reactivity@3.5.40':
+ resolution: {integrity: sha512-B7ot9UlUZOi1zbq61/LvE88ZLTV8IlajTdiZTAEiDQgrnIMIZoPr9kGw0Zw46ObW62O9+H/Be3kMbfb7kYPQZA==}
- '@vue/runtime-core@3.5.39':
- resolution: {integrity: sha512-9GLtNyRvPAUMbX+7ono0RC2j0guo2LXVi8LvcmAooImACUKm0oFf0jjwbX8/H0AE/t1nxhAkn8RSl9PMCzzxZw==}
+ '@vue/runtime-core@3.5.40':
+ resolution: {integrity: sha512-KAZLweuZ6uUJPK1PMSQPgBU5gCjgrrfjUhSglmU9NhH+Zjepa8cnwSydPWDWHDwOgY4g3VcZ+PljbiHlURNCbw==}
- '@vue/runtime-dom@3.5.39':
- resolution: {integrity: sha512-7Y6aAGboKcXAZ3ECuUy7RrS5yy2r47dhTp2SKaJmYxjopImaVFaNa5Ne66NwGovsrxVAl5S5rwc7m22UG7Lmww==}
+ '@vue/runtime-dom@3.5.40':
+ resolution: {integrity: sha512-ZfrX8ssZQds900L9pr8AuK05ddnMsR4MPMZr8cPN9GoqoPWcXLhjvvbIA2SMv+7a97sJ1vv9pj/zxK0Cq/eEFQ==}
- '@vue/server-renderer@3.5.39':
- resolution: {integrity: sha512-yZSakiAGw85rZfG7UM8akMnIF+FmeiNk47uvHf2nVBBSe+dIKUhZuZq9+XgJhbV3nS5Z4ALH23/MpXofW+mbcw==}
- peerDependencies:
- vue: 3.5.39
+ '@vue/server-renderer@3.5.40':
+ resolution: {integrity: sha512-XNJym9WpevhTVt1HuwOrCRJ5Q+9z4BjTMrDtjTrvx74SmUll8spNTw6whWJa9mEkO4PKn5TihI/bm/8ds2QVJw==}
- '@vue/shared@3.5.39':
- resolution: {integrity: sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==}
+ '@vue/shared@3.5.40':
+ resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==}
'@vue/test-utils@2.4.11':
resolution: {integrity: sha512-GDqaqZsA6m2E5vNzej0aYiIb6BX8xV9pNSbbbXKOfEYwg7ZNblVX8suyqmUBThq8VIrgAJNxn+z72hVtUeiWHA==}
@@ -1246,9 +1231,8 @@ packages:
'@zarrita/storage@0.2.0':
resolution: {integrity: sha512-855ZXqtnds7spnT8vNvD+MXa3QExP1m2GqShe8yt7uZXHnQLgJHgkpVwFjE1B0KDDRO0ki09hmk6OboTaIfPsQ==}
- abbrev@2.0.0:
- resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
- engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ abbrev@1.0.3:
+ resolution: {integrity: sha512-s07HMJf6O5iTLVDx9cH7c9VbOdrmzxE+AzWz9CPi94zVNBQQA3jIwIZKTrHQj4dGR1T/MdwMnVJzSpjaVEXtXw==}
abbrev@4.0.0:
resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==}
@@ -1274,6 +1258,11 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ acorn@8.17.0:
+ resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
agent-base@7.0.2:
resolution: {integrity: sha512-k2/tQ1+8Zf50dEUJWklUP80LcE/+Ph+OJ6cf2Ff2fD/c/TtCe6ofnCoNMz9UnyxOQYlaAALZtEWETzn+1JjfHg==}
engines: {node: '>= 14'}
@@ -1282,15 +1271,23 @@ packages:
resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
engines: {node: '>= 14'}
+ agent-base@7.1.1:
+ resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
+ engines: {node: '>= 14'}
+
ajv@6.14.0:
resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
- ajv@8.20.0:
- resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
+ ajv@8.18.0:
+ resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
alien-signals@3.2.1:
resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==}
+ amdefine@1.0.1:
+ resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==}
+ engines: {node: '>=0.4.2'}
+
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -1303,6 +1300,10 @@ packages:
resolution: {integrity: sha512-Qts4KCLKG+waHc9C4m07weIY8qyeixoS0h6RnbsNVD6Fw+pEZGW3vTyObL3WXpE09Mq4Oi7/lBEyLmOiLtlYWQ==}
engines: {node: '>=8'}
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
ansi-styles@6.1.0:
resolution: {integrity: sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==}
engines: {node: '>=12'}
@@ -1317,21 +1318,17 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- array-buffer-byte-length@1.0.2:
- resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
- engines: {node: '>= 0.4'}
-
- arraybuffer.prototype.slice@1.0.4:
- resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
- engines: {node: '>= 0.4'}
-
asn1js@3.0.10:
resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==}
engines: {node: '>=12.0.0'}
- ast-types@0.8.15:
- resolution: {integrity: sha512-8WsusRFHT6D2CpPTCLLLeIp4dN4pMEgmVX/jaSBsbMFObktStNdGOE1ZW4x8V/RABr1VtqruQgpabZyvzrrrww==}
- engines: {node: '>= 0.8'}
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ ast-types@0.6.12:
+ resolution: {integrity: sha512-HqoNhqeCHNYJ8qnY6zM3PZbFQmRpnON8CuRECarbdO3CugpPMfDqU/DbokpKBdnRycojPsIoTp2hSqUgJ1nqMg==}
+ engines: {node: '>= 0.6'}
ast-v8-to-istanbul@1.0.0:
resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==}
@@ -1340,12 +1337,8 @@ packages:
resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==}
engines: {node: '>=0.12.0'}
- async-function@1.0.0:
- resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
- engines: {node: '>= 0.4'}
-
- async@3.2.6:
- resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
+ async@3.2.3:
+ resolution: {integrity: sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
@@ -1354,19 +1347,15 @@ packages:
resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
engines: {node: '>= 4.0.0'}
- available-typed-arrays@1.0.7:
- resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
- engines: {node: '>= 0.4'}
-
aws4@1.13.2:
resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
balanced-match@1.0.0:
resolution: {integrity: sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==}
- balanced-match@4.0.2:
- resolution: {integrity: sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==}
- engines: {node: 20 || >=22}
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -1383,13 +1372,16 @@ packages:
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
- boolean@3.2.0:
- resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==}
+ boolean@3.0.1:
+ resolution: {integrity: sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==}
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+ brace-expansion@1.1.13:
+ resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==}
+
brace-expansion@5.0.7:
resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==}
engines: {node: 18 || 20 || >=22}
@@ -1405,44 +1397,28 @@ packages:
resolution: {integrity: sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw==}
engines: {node: '>=14.0.0'}
- byte-counter@0.1.0:
- resolution: {integrity: sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==}
- engines: {node: '>=20'}
-
bytestreamjs@2.0.1:
resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==}
engines: {node: '>=6.0.0'}
- cacheable-lookup@7.0.0:
- resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
- engines: {node: '>=14.16'}
+ cacache@20.0.4:
+ resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==}
+ engines: {node: ^20.17.0 || >=22.9.0}
- cacheable-request@13.0.19:
- resolution: {integrity: sha512-SVXGH037+Mo1aIMO5B2UcleR43FGjFdN+M8JObSyEoQ2Mn4CODRWx28gN5jiTF0n5ItsgtIZfyargMNs8GX4kg==}
- engines: {node: '>=18'}
+ cacheable-lookup@5.0.3:
+ resolution: {integrity: sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w==}
+ engines: {node: '>=10'}
- call-bind-apply-helpers@1.0.0:
- resolution: {integrity: sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==}
- engines: {node: '>= 0.4'}
+ cacheable-request@7.0.2:
+ resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==}
+ engines: {node: '>=8'}
call-bind-apply-helpers@1.0.1:
resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==}
engines: {node: '>= 0.4'}
- call-bind-apply-helpers@1.0.2:
- resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
- engines: {node: '>= 0.4'}
-
- call-bind@1.0.9:
- resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==}
- engines: {node: '>= 0.4'}
-
- call-bound@1.0.4:
- resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
- engines: {node: '>= 0.4'}
-
- callsites@3.0.0:
- resolution: {integrity: sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==}
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
camelcase@5.0.0:
@@ -1453,10 +1429,6 @@ packages:
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
engines: {node: '>=18'}
- chalk@4.0.0:
- resolution: {integrity: sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==}
- engines: {node: '>=10'}
-
chalk@4.1.1:
resolution: {integrity: sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==}
engines: {node: '>=10'}
@@ -1472,18 +1444,14 @@ packages:
chromium-pickle-js@0.2.0:
resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==}
- chunk-data@0.1.0:
- resolution: {integrity: sha512-zFyPtyC0SZ6Zu79b9sOYtXZcgrsXe0RpePrzRyj52hYVFG1+Rk6rBqjjOEk+GNQwc3PIX+86teQMok970pod1g==}
- engines: {node: '>=20'}
+ ci-info@4.2.0:
+ resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==}
+ engines: {node: '>=8'}
ci-info@4.3.1:
resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==}
engines: {node: '>=8'}
- ci-info@4.4.0:
- resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
- engines: {node: '>=8'}
-
cli-table3@0.5.0:
resolution: {integrity: sha512-c7YHpUyO1SaKaO7kYtxd5NZ8FjAmSK3LpKkuzdwn+2CwpFxBpdoQLm+OAnnCfoEl7onKhN9PKQi1lsHuAIUqGQ==}
engines: {node: '>=6'}
@@ -1498,6 +1466,9 @@ packages:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
+ clone-response@1.0.2:
+ resolution: {integrity: sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==}
+
code-point-at@1.0.0:
resolution: {integrity: sha512-vPAxLvwyfvkfihd9N50TmgjpGsvU3whDvdNCf3fAEjlKt/PH33+xCrtDY9FBL59xjs9wprnMKnK/u43TPFFWuw==}
engines: {node: '>=0.10.0'}
@@ -1521,9 +1492,9 @@ packages:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
- commander@10.0.0:
- resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
- engines: {node: '>=14'}
+ commander@11.0.0:
+ resolution: {integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==}
+ engines: {node: '>=16'}
commander@2.20.0:
resolution: {integrity: sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==}
@@ -1591,18 +1562,6 @@ packages:
resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
- data-view-buffer@1.0.2:
- resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
- engines: {node: '>= 0.4'}
-
- data-view-byte-length@1.0.2:
- resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
- engines: {node: '>= 0.4'}
-
- data-view-byte-offset@1.0.1:
- resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
- engines: {node: '>= 0.4'}
-
dayjs@1.11.21:
resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==}
@@ -1615,6 +1574,15 @@ packages:
supports-color:
optional: true
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
decamelize@1.2.0:
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
engines: {node: '>=0.10.0'}
@@ -1622,19 +1590,23 @@ packages:
decimal.js@10.6.0:
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
- decompress-response@10.0.0:
- resolution: {integrity: sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q==}
- engines: {node: '>=20'}
+ decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
- deep-is@0.1.3:
- resolution: {integrity: sha512-GtxAN4HvBachZzm4OnWqc45ESpUCMwkYcsjnsPs23FwJbsO+k4t0k9bQCgOmzIlpHO28+WPK/KRbRk0DDHuuDw==}
+ defer-to-connect@2.0.0:
+ resolution: {integrity: sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==}
+ engines: {node: '>=10'}
- define-data-property@1.1.4:
- resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ define-properties@1.1.2:
+ resolution: {integrity: sha512-hpr5VSFXGamODSCN6P2zdSBY6zJT7DlcBAHiPIa2PWDvfBqJQntSK0ehUoHoS6HGeSS19dgj7E+1xOjfG3zEtQ==}
engines: {node: '>= 0.4'}
- define-properties@1.2.1:
- resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ define-properties@1.1.3:
+ resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==}
engines: {node: '>= 0.4'}
delayed-stream@1.0.0:
@@ -1645,8 +1617,8 @@ packages:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
- detect-node@2.1.0:
- resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==}
+ detect-node@2.0.4:
+ resolution: {integrity: sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==}
dijkstrajs@1.0.1:
resolution: {integrity: sha512-goNHV5cW23Dnwq2hgLhOmV/ACzI7VrGKIPLnuUPg+CYyx7dO3Y5VQGNU7VY90cCfhQxGhqSrnwJMN+wTgXq68g==}
@@ -1657,11 +1629,11 @@ packages:
dmg-builder@26.15.3:
resolution: {integrity: sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ==}
- dompurify@3.4.11:
- resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==}
+ dompurify@3.4.12:
+ resolution: {integrity: sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==}
- dotenv-expand@11.0.7:
- resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==}
+ dotenv-expand@11.0.6:
+ resolution: {integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==}
engines: {node: '>=12'}
dotenv@16.6.1:
@@ -1672,21 +1644,17 @@ packages:
resolution: {integrity: sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==}
engines: {node: '>= 0.4'}
- dunder-proto@1.0.1:
- resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
- engines: {node: '>= 0.4'}
-
duplexer2@0.1.4:
resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==}
- earcut@3.1.0:
- resolution: {integrity: sha512-zzWyF8F243oYbheQRbqrThgEFN3eJ5m7C6o/JeWbJ4a8ltsKDgHil3sT/K1qUmqrldtkUv2/MO70uZ1gOva1hA==}
+ earcut@3.0.0:
+ resolution: {integrity: sha512-41Fs7Q/PLq1SDbqjsgcY7GA42T0jvaCNGXgGtsNdvg+Yv8eIu06bxv4/PoREkZ9nMDNwnUSG9OFB9+yv8eKhDg==}
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
- editorconfig@1.0.7:
- resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==}
+ editorconfig@1.0.3:
+ resolution: {integrity: sha512-SLHUig+v3PpjlCGenNDSMVj5caWTJ+aDXaqR1ucZCbXcotV3D7+ycT1jwbICxiPC6gju/rS+iRw8SC7kQukSig==}
engines: {node: '>=14'}
hasBin: true
@@ -1730,8 +1698,11 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
- enhanced-resolve@5.19.0:
- resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==}
+ end-of-stream@1.1.0:
+ resolution: {integrity: sha512-EoulkdKF/1xa92q25PbjuDcgJ9RDHYU2Rs3SCIvs2/dSQ3BpmxneNHmA/M7fe60M3PrV7nNGTTNbkK62l6vXiQ==}
+
+ enhanced-resolve@5.24.1:
+ resolution: {integrity: sha512-7DdUaTjmNwMcH2gLr1qycesKII3BK4RLy/mdAb7x10Lq7bR4aNKHt1BR1ZALSv0rPM/hF5wYF0PhGop/rJm8vw==}
engines: {node: '>=10.13.0'}
entities@7.0.1:
@@ -1746,15 +1717,11 @@ packages:
resolution: {integrity: sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==}
engines: {node: '>=6'}
- err-code@2.0.3:
- resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
+ err-code@2.0.2:
+ resolution: {integrity: sha512-cSVM85xDoIsi+WrRBwhfu4yVaW9NJaMjtGgHfGKkfNeSGwAnK5RDRJfAVsvvVU1LvREs51m2brj9nU251Y+eCA==}
- es-abstract-get@1.0.0:
- resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==}
- engines: {node: '>= 0.4'}
-
- es-abstract@1.24.2:
- resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==}
+ es-abstract@1.5.0:
+ resolution: {integrity: sha512-hOEuDmmeU5AizmgMRq+4QmlXUHZyYdZ0I2WtxcYcpF7ktDkBzyLQsybaaekIFkJebo5NkeXMqGZge2U4lQUFUw==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
@@ -1765,23 +1732,19 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
- es-module-lexer@2.2.0:
- resolution: {integrity: sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==}
+ es-module-lexer@2.0.0:
+ resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==}
es-object-atoms@1.0.0:
resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
engines: {node: '>= 0.4'}
- es-object-atoms@1.1.2:
- resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
- engines: {node: '>= 0.4'}
-
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
- es-to-primitive@1.3.4:
- resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==}
+ es-to-primitive@1.1.0:
+ resolution: {integrity: sha512-1v1In8XPra1MpmxdNv2nPZIW2vGrA4/ePPsZgn/EfPS6Voj8sqidt98nhcRZI2u/EkvcnoourE086KqzYJvrSA==}
engines: {node: '>= 0.4'}
es6-error@4.1.1:
@@ -1819,8 +1782,8 @@ packages:
resolution: {integrity: sha512-XjVGBhtDZJfyuhIxnQ/WMm385RbX3DBu7H1J7HNNhmB2tnGxMeqVSnYv79oAj992ayvIBZghsymwkYFS6cGH4Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint-plugin-vue@10.9.2:
- resolution: {integrity: sha512-4g7ZP3pYcuqd7Zp0pzUKcos0W+RkjBz4EGdhJ92FcYk6v03Ti/GK5NwjgsjxHK+98eXDbHeK7VtX1az7/8doZA==}
+ eslint-plugin-vue@10.10.0:
+ resolution: {integrity: sha512-dL9x9rBHqqNcByWiLOHK6L0SB97V82/NC0cZRn9cXPjM7pCuWlpQQP9bFH4vjBv80ej1ZpzAkuD8zWH1o9bZbA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
@@ -1841,10 +1804,6 @@ packages:
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint-visitor-keys@3.3.0:
- resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
-
eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1857,8 +1816,8 @@ packages:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- eslint@9.39.4:
- resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==}
+ eslint@9.39.5:
+ resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
peerDependencies:
@@ -1878,19 +1837,19 @@ packages:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- esprima-fb@15001.1001.0-dev-harmony-fb:
- resolution: {integrity: sha512-m7OsYzocA8OQ3+9CxmhIv7NPHtyDR2ixaLCO7kLZ+YH+xQ/BpaZmll9EXmc+kBxzWA8BRBXbNEuEQqQ6vfsgDw==}
+ esprima-fb@12001.1.0-dev-harmony-fb:
+ resolution: {integrity: sha512-kj/HWGgQXRPKf7qAd8Woo2AaxRdT8kpTK2x1+c3zeucEYiIsGktmMC1N3q8J99R0ECMG7Ey/kywsR3qXPHBaXA==}
engines: {node: '>=0.4.0'}
hasBin: true
- esquery@1.5.0:
- resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
- engines: {node: '>=0.10'}
-
esquery@1.6.0:
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
@@ -1913,8 +1872,8 @@ packages:
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
- esutils@2.0.2:
- resolution: {integrity: sha512-UUPPULqkyAV+M3Shodis7l8D+IyX6V8SbaBnTb449jf3fMTd8+UOZI1Q70NbZVOQkcR91yYgdHsJiMMMVmYshg==}
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
execa@0.10.0:
@@ -1939,17 +1898,17 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
- fast-diff@1.3.0:
- resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
+ fast-diff@1.1.2:
+ resolution: {integrity: sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==}
- fast-json-stable-stringify@2.0.0:
- resolution: {integrity: sha512-eIgZvM9C3P05kg0qxfqaVU6Tma4QedCPIByQOcemV0vju8ot3cS2DpHi4m2G2JvbSMI152rjfLX0p1pkSdyPlQ==}
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
fast-levenshtein@2.0.6:
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
- fast-uri@3.1.2:
- resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==}
+ fast-uri@4.1.1:
+ resolution: {integrity: sha512-YPOs1zD5TG2+EZt+r88LwF6mclA7TPkpwMP7ZN3TO2HiHS8TXvq7QA/17iJsV9dubcLo/f8eEYqMBruyQV21hQ==}
fd-package-json@2.0.0:
resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==}
@@ -1974,8 +1933,8 @@ packages:
fflate@0.8.0:
resolution: {integrity: sha512-FAdS4qMuFjsJj6XHbBaZeXOgaypXp8iw/Tpyuq/w3XA41jjLHT8NPA+n7czH/DDhdncq0nAyDZmPeWXh2qmdIg==}
- fflate@0.8.2:
- resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
+ fflate@0.8.3:
+ resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==}
file-entry-cache@8.0.0:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
@@ -1996,16 +1955,18 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
- flat-cache@4.0.0:
- resolution: {integrity: sha512-EryKbCE/wxpxKniQlyas6PY1I9vwtF3uCBweX+N8KYTCn3Y12RTGtQAJ/bd5pl7kxUAc8v/R3Ake/N17OZiFqA==}
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
engines: {node: '>=16'}
flatted@3.4.2:
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
- for-each@0.3.5:
- resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
- engines: {node: '>= 0.4'}
+ for-each@0.3.3:
+ resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+
+ foreach@2.0.6:
+ resolution: {integrity: sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg==}
foreground-child@3.1.0:
resolution: {integrity: sha512-lXeSPRCndWPaipZbtI4CkvTZpF6OPsy19dkvf7+5AHeJD+w+iAKPc9Q78xWBmX4SdR+8xrtY9jTXs/YDv8q+Ug==}
@@ -2028,8 +1989,8 @@ packages:
resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==}
engines: {node: '>=14.14'}
- fs-extra@11.3.1:
- resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==}
+ fs-extra@11.2.0:
+ resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
engines: {node: '>=14.14'}
fs-extra@7.0.1:
@@ -2048,6 +2009,10 @@ packages:
resolution: {integrity: sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==}
engines: {node: '>=10'}
+ fs-minipass@3.0.3:
+ resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
@@ -2061,23 +2026,12 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
- function-bind@1.1.1:
- resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+ function-bind@1.0.2:
+ resolution: {integrity: sha512-v2124bSW+kLVmfLEHDpsTeQy+sLEg9gdD/1aVTO7jolX/EmBEq9+atKWYEV3w791Os5USi8yNyuUtiVEXMWiAw==}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
- function.prototype.name@1.2.0:
- resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==}
- engines: {node: '>= 0.4'}
-
- functions-have-names@1.2.3:
- resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
-
- generator-function@2.0.0:
- resolution: {integrity: sha512-xPypGGincdfyl/AiSGa7GjXLkvld9V7GjZlowup9SHIJnQnHLFiLODCd/DqKOp0PBagbHJ68r1KJI9Mut7m4sA==}
- engines: {node: '>= 0.4'}
-
geotiff@3.0.5:
resolution: {integrity: sha512-OWcL9S9+yDZ6iAlXMt32T1iwUApJM8UiD47xbm6ZP1h33d10fqkPs14EG/ttT5EnefpZSx3G15iDFC5FxUNUwA==}
engines: {node: '>=10.19'}
@@ -2093,32 +2047,17 @@ packages:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
- get-intrinsic@1.1.3:
- resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==}
-
get-intrinsic@1.2.6:
resolution: {integrity: sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==}
engines: {node: '>= 0.4'}
- get-intrinsic@1.3.0:
- resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
- engines: {node: '>= 0.4'}
-
- get-proto@1.0.1:
- resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
- engines: {node: '>= 0.4'}
-
get-stream@3.0.0:
resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
engines: {node: '>=4'}
- get-stream@9.0.1:
- resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
- engines: {node: '>=18'}
-
- get-symbol-description@1.1.0:
- resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
- engines: {node: '>= 0.4'}
+ get-stream@5.1.0:
+ resolution: {integrity: sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==}
+ engines: {node: '>=8'}
get-tsconfig@4.14.0:
resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
@@ -2144,24 +2083,21 @@ packages:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
- globals@17.6.0:
- resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==}
+ globals@17.7.0:
+ resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==}
engines: {node: '>=18'}
- globalthis@1.0.4:
- resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+ globalthis@1.0.1:
+ resolution: {integrity: sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==}
engines: {node: '>= 0.4'}
- gopd@1.0.1:
- resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
-
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
- got@15.0.7:
- resolution: {integrity: sha512-HA4XUhL2xk0/sA4l3zRyOzfqPtDEiVYpzDae56WLX2aHZYF6v6hjTnuDvURIdznSTPFYW1JUdlREhZjGAF9LCQ==}
- engines: {node: '>=22'}
+ got@11.8.6:
+ resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==}
+ engines: {node: '>=10.19.0'}
graceful-fs@4.1.2:
resolution: {integrity: sha512-M0F8L0fpTAUAkc73Qw6272c7+zc2Se6gX4HIsfpv8vRubJZwU8wkvjbKTvaotgqBw6H8brH8hKL1qbt4HIGnEw==}
@@ -2174,6 +2110,9 @@ packages:
graceful-fs@4.2.0:
resolution: {integrity: sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==}
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
graceful-fs@4.2.2:
resolution: {integrity: sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==}
@@ -2183,21 +2122,10 @@ packages:
graceful-fs@4.2.6:
resolution: {integrity: sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==}
- has-bigints@1.1.0:
- resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
- engines: {node: '>= 0.4'}
-
has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'}
- has-property-descriptors@1.0.2:
- resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
-
- has-proto@1.2.0:
- resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
- engines: {node: '>= 0.4'}
-
has-symbols@1.0.3:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
@@ -2210,21 +2138,12 @@ packages:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
- has@1.0.3:
- resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
- engines: {node: '>= 0.4.0'}
-
- hasown@2.0.2:
- resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
- engines: {node: '>= 0.4'}
-
hasown@2.0.4:
resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
engines: {node: '>= 0.4'}
- hosted-git-info@10.1.1:
- resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==}
- engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0}
+ hosted-git-info@2.8.9:
+ resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
html-encoding-sniffer@6.0.0:
resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
@@ -2240,23 +2159,31 @@ packages:
resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
engines: {node: '>= 14'}
- http2-wrapper@2.2.1:
- resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
+ http2-wrapper@1.0.0-beta.5.2:
+ resolution: {integrity: sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==}
engines: {node: '>=10.19.0'}
https-proxy-agent@7.0.0:
resolution: {integrity: sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==}
engines: {node: '>= 14'}
- ignore@5.2.0:
- resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==}
+ https-proxy-agent@7.0.1:
+ resolution: {integrity: sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ==}
+ engines: {node: '>= 14'}
+
+ iconv-lite@0.7.2:
+ resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
+ engines: {node: '>=0.10.0'}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
immediate@3.0.5:
resolution: {integrity: sha512-Ry9armVU2RUoNbXoNse76rLHch0dwRTR9/zBUk7M2sUYZNcYJJXeJlSnHycGp7INEN3XVmwCjDvNerEUTPwDlg==}
- import-fresh@3.2.1:
- resolution: {integrity: sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==}
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
imurmurhash@0.1.4:
@@ -2282,62 +2209,34 @@ packages:
inline-process-browser@1.0.0:
resolution: {integrity: sha512-DyFIA2uFCdhB57k1blue0nbd/syuws7NIlE0UkWSWbiM++mbo8W6Zl0QZ/7DwveucRC7YUi87OgJvJyERnwCbg==}
- internal-slot@1.1.0:
- resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
- engines: {node: '>= 0.4'}
-
invert-kv@2.0.0:
resolution: {integrity: sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==}
engines: {node: '>=4'}
- is-array-buffer@3.0.5:
- resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
- engines: {node: '>= 0.4'}
-
- is-async-function@2.1.1:
- resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
- engines: {node: '>= 0.4'}
-
- is-bigint@1.1.0:
- resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
- engines: {node: '>= 0.4'}
+ ip-address@9.0.5:
+ resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
+ engines: {node: '>= 12'}
is-blob@2.1.0:
resolution: {integrity: sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==}
engines: {node: '>=6'}
- is-boolean-object@1.2.0:
- resolution: {integrity: sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==}
- engines: {node: '>= 0.4'}
-
- is-boolean-object@1.2.2:
- resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
- engines: {node: '>= 0.4'}
-
- is-callable@1.2.7:
- resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
- engines: {node: '>= 0.4'}
-
- is-data-view@1.0.2:
- resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
+ is-callable@1.1.1:
+ resolution: {integrity: sha512-tEGMwQw21bO7B26l0Atb8JujyIhdIj4lLR2gkvW7vI6fyuDhNE5cahVCkGZe1eWT99Li6mCpu9TkFL3tbABcxQ==}
engines: {node: '>= 0.4'}
- is-date-object@1.1.0:
- resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
+ is-callable@1.1.3:
+ resolution: {integrity: sha512-gcmUh1kFielP0yJSKD+A1aOPNlI8ZzruhHum+Geq6M3Ibx5JnwcsTJCktWj+reKIjjtefToy/u8YNRUZq4FHuQ==}
engines: {node: '>= 0.4'}
- is-document.all@1.0.0:
- resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==}
+ is-date-object@1.0.1:
+ resolution: {integrity: sha512-P5rExV1phPi42ppoMWy7V63N3i173RY921l4JJ7zonMSxK+OWGPj76GD+cUKUb68l4vQXcJp2SsG+r/A4ABVzg==}
engines: {node: '>= 0.4'}
is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
- is-finalizationregistry@1.1.1:
- resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
- engines: {node: '>= 0.4'}
-
is-fullwidth-code-point@1.0.0:
resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==}
engines: {node: '>=0.10.0'}
@@ -2350,83 +2249,23 @@ packages:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
- is-generator-function@1.1.2:
- resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==}
- engines: {node: '>= 0.4'}
-
- is-glob@4.0.0:
- resolution: {integrity: sha512-IEg9HSCKitWUYBRkCSztkm2Lenav8e04mlxHjiMRg2w9Bx82TFIDEDamwfn0RgwFgLNLSkZd0YJT2ColdN8KCw==}
- engines: {node: '>=0.10.0'}
-
is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
- is-map@2.0.3:
- resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
- engines: {node: '>= 0.4'}
-
- is-negative-zero@2.0.3:
- resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
- engines: {node: '>= 0.4'}
-
- is-number-object@1.1.0:
- resolution: {integrity: sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==}
- engines: {node: '>= 0.4'}
-
- is-number-object@1.1.1:
- resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
- engines: {node: '>= 0.4'}
-
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
- is-regex@1.2.1:
- resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
- engines: {node: '>= 0.4'}
-
- is-set@2.0.3:
- resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
- engines: {node: '>= 0.4'}
-
- is-shared-array-buffer@1.0.4:
- resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
+ is-regex@1.0.3:
+ resolution: {integrity: sha512-FjfD1xlC5B76bwdsrIMBUyoG1HLKKqLi1p669fku0BqDk9wJb0XnuUanh3OTCjoEUCo0r8nKunaah8BmPhj5cQ==}
engines: {node: '>= 0.4'}
is-stream@1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
- is-stream@4.0.1:
- resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
- engines: {node: '>=18'}
-
- is-string@1.1.1:
- resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
- engines: {node: '>= 0.4'}
-
- is-symbol@1.1.0:
- resolution: {integrity: sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==}
- engines: {node: '>= 0.4'}
-
- is-symbol@1.1.1:
- resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
- engines: {node: '>= 0.4'}
-
- is-typed-array@1.1.15:
- resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
- engines: {node: '>= 0.4'}
-
- is-weakmap@2.0.2:
- resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
- engines: {node: '>= 0.4'}
-
- is-weakref@1.1.1:
- resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
- engines: {node: '>= 0.4'}
-
- is-weakset@2.0.4:
- resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
+ is-symbol@1.0.1:
+ resolution: {integrity: sha512-Z1cLAG7dXM1vJv8mAGjA+XteO0YzYPwD473+qPAWKycNZxwCH/I56QIohKGYCZSB7j6tajrxi/FvOpAfqGhhRQ==}
engines: {node: '>= 0.4'}
isarray@0.0.1:
@@ -2435,16 +2274,13 @@ packages:
isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
- isarray@2.0.5:
- resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
-
isbinaryfile@4.0.8:
resolution: {integrity: sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w==}
engines: {node: '>= 8.0.0'}
- isbinaryfile@5.0.7:
- resolution: {integrity: sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==}
- engines: {node: '>= 18.0.0'}
+ isbinaryfile@5.0.0:
+ resolution: {integrity: sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg==}
+ engines: {node: '>= 14.0.0'}
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -2453,6 +2289,10 @@ packages:
resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
engines: {node: '>=16'}
+ isexe@3.1.5:
+ resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==}
+ engines: {node: '>=18'}
+
istanbul-lib-coverage@3.2.2:
resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
engines: {node: '>=8'}
@@ -2469,10 +2309,6 @@ packages:
resolution: {integrity: sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==}
engines: {node: '>=14'}
- jackspeak@4.2.3:
- resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==}
- engines: {node: 20 || >=22}
-
jake@10.8.5:
resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
engines: {node: '>=10'}
@@ -2482,22 +2318,15 @@ packages:
resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
hasBin: true
- jiti@2.6.1:
- resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
- hasBin: true
-
jiti@2.7.0:
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true
- js-beautify@1.15.4:
- resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
- engines: {node: '>=14'}
+ js-beautify@1.14.9:
+ resolution: {integrity: sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==}
+ engines: {node: '>=12'}
hasBin: true
- js-cookie@3.0.8:
- resolution: {integrity: sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==}
-
js-tokens@10.0.0:
resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==}
@@ -2505,6 +2334,13 @@ packages:
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
hasBin: true
+ js-yaml@5.2.1:
+ resolution: {integrity: sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==}
+ hasBin: true
+
+ jsbn@1.1.0:
+ resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
+
jsdom@29.1.1:
resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0}
@@ -2552,14 +2388,14 @@ packages:
keycharm@0.4.0:
resolution: {integrity: sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==}
+ keyv@4.0.0:
+ resolution: {integrity: sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog==}
+
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
- keyv@5.6.0:
- resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==}
-
- knip@6.24.0:
- resolution: {integrity: sha512-PokLlgeEjLh1rAsB7ts+52wZ37HBr1nDhE6NNONwEaXdeZGCJOkP7ZlIAI2Gtu8xohquzTWy75bc/1diI9shQw==}
+ knip@6.29.0:
+ resolution: {integrity: sha512-A3kXqSBky1tWBAqiU9srdtu0Larhzkuyor0aD/gg+ToiqyBncCCs2Q60sLsxmcKhV0OsKss9LV0hMPpwLv711Q==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
@@ -2586,30 +2422,60 @@ packages:
cpu: [arm64]
os: [android]
+ lightningcss-android-arm64@1.33.0:
+ resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
lightningcss-darwin-arm64@1.32.0:
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [darwin]
+ lightningcss-darwin-arm64@1.33.0:
+ resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
lightningcss-darwin-x64@1.32.0:
resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [darwin]
+ lightningcss-darwin-x64@1.33.0:
+ resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
lightningcss-freebsd-x64@1.32.0:
resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [freebsd]
+ lightningcss-freebsd-x64@1.33.0:
+ resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
lightningcss-linux-arm-gnueabihf@1.32.0:
resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
engines: {node: '>= 12.0.0'}
cpu: [arm]
os: [linux]
+ lightningcss-linux-arm-gnueabihf@1.33.0:
+ resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
lightningcss-linux-arm64-gnu@1.32.0:
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
engines: {node: '>= 12.0.0'}
@@ -2617,6 +2483,13 @@ packages:
os: [linux]
libc: [glibc]
+ lightningcss-linux-arm64-gnu@1.33.0:
+ resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
@@ -2624,6 +2497,13 @@ packages:
os: [linux]
libc: [musl]
+ lightningcss-linux-arm64-musl@1.33.0:
+ resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
@@ -2631,6 +2511,13 @@ packages:
os: [linux]
libc: [glibc]
+ lightningcss-linux-x64-gnu@1.33.0:
+ resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
@@ -2638,23 +2525,46 @@ packages:
os: [linux]
libc: [musl]
+ lightningcss-linux-x64-musl@1.33.0:
+ resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [win32]
+ lightningcss-win32-arm64-msvc@1.33.0:
+ resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
lightningcss-win32-x64-msvc@1.32.0:
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [win32]
+ lightningcss-win32-x64-msvc@1.33.0:
+ resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
lightningcss@1.32.0:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
- locate-path@3.0.0:
+ lightningcss@1.33.0:
+ resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
+ engines: {node: '>= 12.0.0'}
+
+ locate-path@3.0.0:
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
engines: {node: '>=6'}
@@ -2676,13 +2586,9 @@ packages:
lodash@4.18.1:
resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==}
- lowercase-keys@3.0.0:
- resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- lowercase-keys@4.0.1:
- resolution: {integrity: sha512-wI9Nui/L8VfADa/cr/7NQruaASk1k23/Uh1khQ02BCVYiiy8F4AhOGnQzJy3Fl/c44GnYSbZHv8g7EcG3kJ1Qg==}
- engines: {node: '>=20'}
+ lowercase-keys@2.0.0:
+ resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
+ engines: {node: '>=8'}
lru-cache@10.2.0:
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
@@ -2692,6 +2598,10 @@ packages:
resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==}
engines: {node: 20 || >=22}
+ lru-cache@11.2.1:
+ resolution: {integrity: sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==}
+ engines: {node: 20 || >=22}
+
lru-cache@11.3.5:
resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==}
engines: {node: 20 || >=22}
@@ -2710,11 +2620,15 @@ packages:
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
engines: {node: '>=10'}
+ make-fetch-happen@15.0.6:
+ resolution: {integrity: sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
mapbox-to-css-font@3.2.0:
resolution: {integrity: sha512-kvsEfzvLik34BiFj+S19bv5d70l9qSdkUzrq99dvZ9d5POaLyB4vJMQmq3BoJ5D6lFG1GYnMM7o7cm5Jh8YEEg==}
- marked@18.0.5:
- resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==}
+ marked@18.0.7:
+ resolution: {integrity: sha512-iDVQ5ldaiKXn6b2JroX5kgRfmwgqolW7NpaEzTl1k/2Zh1njIEN9yniyLV/mOvWwtsE8OGgkjsCYvijuPk1dtA==}
engines: {node: '>= 20'}
hasBin: true
@@ -2726,10 +2640,6 @@ packages:
resolution: {integrity: sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==}
engines: {node: '>= 0.4'}
- math-intrinsics@1.1.0:
- resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
- engines: {node: '>= 0.4'}
-
mdn-data@2.27.1:
resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
@@ -2749,18 +2659,22 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
- mime@2.6.0:
- resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
+ mime@2.5.2:
+ resolution: {integrity: sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==}
engines: {node: '>=4.0.0'}
hasBin: true
- mimic-fn@1.2.0:
- resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==}
+ mimic-fn@1.0.0:
+ resolution: {integrity: sha512-LEeiAHbBHYckIvV1OfM26E7F4pH19hVFZuOMyMYhXxF9ztFz12Y8yjRAqoftbLMbKkDhITd0+ND+P+O6J6tetg==}
engines: {node: '>=4'}
- mimic-response@4.0.0:
- resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ mimic-response@1.0.0:
+ resolution: {integrity: sha512-Nyxh+TsqCmcTqEqpGdDSRuqt044C42ppSMT/6f4valegHIStgaCH31fvW4ZKL+SH3/eH4hqHDT2LMPf+93shDQ==}
+ engines: {node: '>=4'}
+
+ mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
mini-svg-data-uri@1.2.3:
resolution: {integrity: sha512-zd6KCAyXgmq6FV1mR10oKXYtvmA9vRoB6xPSTUJTbFApCtkefDnYueVR1gkof3KcdLZo1Y8mjF2DFmQMIxsHNQ==}
@@ -2770,25 +2684,64 @@ packages:
resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==}
engines: {node: 18 || 20 || >=22}
+ minimatch@10.2.5:
+ resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
+ engines: {node: 18 || 20 || >=22}
+
minimatch@3.1.4:
resolution: {integrity: sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==}
minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+ minipass-collect@2.0.1:
+ resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass-fetch@5.0.2:
+ resolution: {integrity: sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
+ minipass-flush@1.0.7:
+ resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==}
+ engines: {node: '>= 8'}
+
+ minipass-pipeline@1.2.4:
+ resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
+ engines: {node: '>=8'}
+
+ minipass-sized@2.0.0:
+ resolution: {integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==}
+ engines: {node: '>=8'}
+
+ minipass@3.0.0:
+ resolution: {integrity: sha512-FKNU4XrAPDX0+ynwns7njVu4RolyG1mUKSlT6n6GwGXLtYSYh2Znc0S83Rl6zEr1zgFfXvAzIBabnmItm+n19g==}
+ engines: {node: '>=8'}
+
+ minipass@7.0.2:
+ resolution: {integrity: sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.0.3:
+ resolution: {integrity: sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.0.4:
+ resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
+ minizlib@3.0.1:
+ resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
+ engines: {node: '>= 18'}
+
minizlib@3.1.0:
resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
engines: {node: '>= 18'}
- mkdirp@0.5.1:
- resolution: {integrity: sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==}
- deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)
- hasBin: true
-
mrmime@2.0.0:
resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
engines: {node: '>=10'}
@@ -2796,6 +2749,9 @@ packages:
ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
@@ -2804,27 +2760,36 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ nanoid@3.3.16:
+ resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
- node-abi@4.31.0:
- resolution: {integrity: sha512-Erq5w/t3syw3s4sDsUaX4QttIdBPsGKTT1DTRsCkTonGggczhlDKm/wDX3o+HPJpQ41EjXCbcmXf0tgr5YZJXw==}
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ node-abi@4.2.0:
+ resolution: {integrity: sha512-admQxilhDcmFJbUl4LQzGu+QyEijW9rctKRH2P7LNavAvln1bdK9OcujM3yi2KysKI41dxTrDtp6QfGEZeCbkg==}
engines: {node: '>=22.12.0'}
node-api-version@0.2.1:
resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==}
- node-gyp@12.4.0:
- resolution: {integrity: sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==}
+ node-gyp@12.2.0:
+ resolution: {integrity: sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==}
engines: {node: ^20.17.0 || >=22.9.0}
hasBin: true
node-int64@0.4.0:
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
- nopt@7.2.1:
- resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
- engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ nopt@6.0.0:
+ resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==}
+ engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
hasBin: true
nopt@9.0.0:
@@ -2832,12 +2797,12 @@ packages:
engines: {node: ^20.17.0 || >=22.9.0}
hasBin: true
- normalize-url@8.1.1:
- resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==}
- engines: {node: '>=14.16'}
+ normalize-url@6.0.1:
+ resolution: {integrity: sha512-VU4pzAuh7Kip71XEmO9aNREYAdMHFGTVj/i+CaTImS8x0i1d3jUZkXhqluy/PRgjPLMgsLQulYY3PJ/aSbSjpQ==}
+ engines: {node: '>=10'}
- npm-run-path@2.0.2:
- resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
+ npm-run-path@2.0.0:
+ resolution: {integrity: sha512-QLqEEWRSpaJc8qzvU+fQSzl6c/UUYcSUz3xaUEpObNl94FDkWx6oLKMpATBh/iH+iPqmHiagetfPIk/sE2j4ug==}
engines: {node: '>=4'}
nth-check@2.1.1:
@@ -2854,16 +2819,12 @@ packages:
resolution: {integrity: sha512-Lbc7GfN7XFaK30bzUN3cDYLOkT0dH05S0ax1QikylHUD9+Z9PRF3G1iYwX3kcz+6AlzTFGkUgMxz6l3aUwbwTA==}
engines: {node: '>=0.10.0'}
- object-inspect@1.13.4:
- resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ object-keys@1.0.12:
+ resolution: {integrity: sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==}
engines: {node: '>= 0.4'}
- object-keys@1.1.1:
- resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
- engines: {node: '>= 0.4'}
-
- object.assign@4.1.7:
- resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
+ object-keys@1.0.8:
+ resolution: {integrity: sha512-yMyMdHyEjnPMnRpKnwOQLtTcS/2DQCItvwFh/A0RFvorh1aWqsIO46ZzfkaT0CmPXcKjCtrq7DhZo+unsR99hA==}
engines: {node: '>= 0.4'}
obug@2.1.1:
@@ -2877,27 +2838,30 @@ packages:
ol@10.9.0:
resolution: {integrity: sha512-svbbgVQUmEHaKpLQ8kRySojs59Brvgl2zYIrqG9eQNXGfsbi55rQasZIDpwpQzDL6OlzrUb0H4hQaiX9wDoGmA==}
- once@1.4.0:
- resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ once@1.3.0:
+ resolution: {integrity: sha512-A31oqbdEQnnhkjIXJ6QKcgO9eN8Xe+dVAQqlFLAmri0Y5s11pUadCihT2popU2WLd5CbbnD2ZVkbEJsR/8JHvA==}
- optionator@0.9.3:
- resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
+ once@1.3.1:
+ resolution: {integrity: sha512-NzfbaaoQvz2JC/D/Yj3GZi0FJG1w9i3K9Bp99Ws3p0xriPynC/YfRcpo2zoVuIduvH4b8+6up4ogGxnqajSKhA==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
engines: {node: '>= 0.8.0'}
os-locale@3.0.0:
resolution: {integrity: sha512-4mi6ZXIp4OtcV/Bwzl9p9Cvae7KJv/czGIm/HK0iaXCuRh7BMpy4l4o4CLjN+atsRQpCW9Rs4FdhfnK0zaR1Jg==}
engines: {node: '>=6'}
- own-keys@1.0.1:
- resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
- engines: {node: '>= 0.4'}
-
- oxc-parser@0.137.0:
- resolution: {integrity: sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==}
+ oxc-parser@0.140.0:
+ resolution: {integrity: sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q==}
engines: {node: ^20.19.0 || >=22.12.0}
- oxc-resolver@11.21.3:
- resolution: {integrity: sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==}
+ oxc-resolver@11.24.2:
+ resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==}
+
+ p-cancelable@2.0.0:
+ resolution: {integrity: sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==}
+ engines: {node: '>=8'}
p-finally@1.0.0:
resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
@@ -2915,10 +2879,6 @@ packages:
resolution: {integrity: sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==}
engines: {node: '>=6'}
- p-limit@3.0.2:
- resolution: {integrity: sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==}
- engines: {node: '>=10'}
-
p-limit@3.1.0:
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
engines: {node: '>=10'}
@@ -2935,14 +2895,14 @@ packages:
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
engines: {node: '>=10'}
+ p-map@7.0.6:
+ resolution: {integrity: sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg==}
+ engines: {node: '>=18'}
+
p-try@2.0.0:
resolution: {integrity: sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==}
engines: {node: '>=6'}
- p-try@2.2.0:
- resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
- engines: {node: '>=6'}
-
package-json-from-dist@1.0.0:
resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
@@ -2952,8 +2912,8 @@ packages:
pako@2.0.4:
resolution: {integrity: sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg==}
- parent-module@1.0.0:
- resolution: {integrity: sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==}
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
parse-headers@2.0.2:
@@ -2985,6 +2945,10 @@ packages:
resolution: {integrity: sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==}
engines: {node: '>=8'}
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
path-scurry@1.11.1:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
@@ -2996,8 +2960,8 @@ packages:
resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==}
hasBin: true
- pe-library@0.4.1:
- resolution: {integrity: sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==}
+ pe-library@0.4.0:
+ resolution: {integrity: sha512-JAmVv2jGxmczplhHO7UoFGJ+pM/yMBpny3vNjwNFuaeQfzKlekQidZ8Ss8EJ0qee8wEQN4lY2IwtWx2oRfMsag==}
engines: {node: '>=12', npm: '>=6'}
picocolors@1.1.1:
@@ -3029,20 +2993,16 @@ packages:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
- possible-typed-array-names@1.1.0:
- resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
- engines: {node: '>= 0.4'}
-
- postcss-selector-parser@7.1.0:
- resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==}
+ postcss-selector-parser@7.1.4:
+ resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==}
engines: {node: '>=4'}
- postcss@8.5.15:
- resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
+ postcss@8.5.19:
+ resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==}
engines: {node: ^10 || ^12 || >=14}
- postcss@8.5.16:
- resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==}
+ postcss@8.5.22:
+ resolution: {integrity: sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==}
engines: {node: ^10 || ^12 || >=14}
postject@1.0.0-alpha.6:
@@ -3058,8 +3018,8 @@ packages:
resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==}
engines: {node: '>=6.0.0'}
- prettier@3.9.3:
- resolution: {integrity: sha512-HWmu+K+zvHNpaMfSnYeqdqrDbR16cuIXaPx8WoHaviQkDJh1/0BNtOZmHVQI5jc3wXv0H1yXc9wjvFdXh+n3hQ==}
+ prettier@3.9.6:
+ resolution: {integrity: sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==}
engines: {node: '>=14'}
hasBin: true
@@ -3067,8 +3027,8 @@ packages:
resolution: {integrity: sha512-j09E78Te59xU4ZesOLN6QVTjRA9zBLAFv3dofAKYTYr8+i9TfW9xd2jikxBpaMYDRqg3eQrq7+CoD5fJh8MWcQ==}
engines: {node: '>= 0.6'}
- proc-log@6.1.0:
- resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==}
+ proc-log@6.0.0:
+ resolution: {integrity: sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==}
engines: {node: ^20.17.0 || >=22.9.0}
process-nextick-args@1.0.0:
@@ -3088,15 +3048,14 @@ packages:
proper-lockfile@4.1.2:
resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
- proto-list@1.2.4:
- resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+ proto-list@1.2.1:
+ resolution: {integrity: sha512-lz+pbjtY8MV0c+P9BsAiBT6F716SBH7iXpkEut63W7khkUB0McsgLkYXZcmVfhClhs4N7E3v9sx26cxxe/Z5oA==}
protocol-buffers-schema@3.6.1:
resolution: {integrity: sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==}
- punycode@2.1.0:
- resolution: {integrity: sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA==}
- engines: {node: '>=6'}
+ pump@3.0.0:
+ resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
@@ -3105,6 +3064,10 @@ packages:
pvtsutils@1.3.6:
resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==}
+ pvutils@1.1.3:
+ resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==}
+ engines: {node: '>=6.0.0'}
+
pvutils@1.1.5:
resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==}
engines: {node: '>=16.0.0'}
@@ -3147,25 +3110,17 @@ packages:
readable-stream@2.3.6:
resolution: {integrity: sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==}
- recast@0.10.43:
- resolution: {integrity: sha512-GC1g4P336t8WOpzVGFOo83m14xQfHbVqe+eDus+4oubobkWb/kONwMWSG6+K3BUtBOoUdUU+GT9kmNCSOBv9+g==}
+ recast@0.10.1:
+ resolution: {integrity: sha512-aU2td+Z2yicHgpxL1w4R3esj/IP2v2HxsO+XXMBbn/M+nj4R5Y8P1got5LYJ+oJl9sglTbUiHY7XVSkjvjECBg==}
engines: {node: '>= 0.8'}
reference-spec-reader@0.2.0:
resolution: {integrity: sha512-q0mfCi5yZSSHXpCyxjgQeaORq3tvDsxDyzaadA/5+AbAUwRyRuuTh0aRQuE/vAOt/qzzxidJ5iDeu1cLHaNBlQ==}
- reflect.getprototypeof@1.0.10:
- resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
- engines: {node: '>= 0.4'}
-
regexp-tree@0.1.1:
resolution: {integrity: sha512-HwRjOquc9QOwKTgbxvZTcddS5mlNlwePMQ3NFL8broajMLD5CXDAqas8Y5yxJH5QtZp5iRor3YCILd5pz71Cgw==}
hasBin: true
- regexp.prototype.flags@1.5.4:
- resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
- engines: {node: '>= 0.4'}
-
require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
@@ -3180,12 +3135,12 @@ packages:
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
- resedit@1.7.2:
- resolution: {integrity: sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==}
+ resedit@1.7.0:
+ resolution: {integrity: sha512-dbsZ0gk5opWPFlKMqvxCrLCuMZUVmsW3yTPT0tT4mYwo5fjQM8c4HMN9ZJt6dRDqDV/78m9SU4rv24PN4NiYaA==}
engines: {node: '>=12', npm: '>=6'}
- resolve-alpn@1.2.1:
- resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
+ resolve-alpn@1.0.0:
+ resolution: {integrity: sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA==}
resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
@@ -3197,9 +3152,8 @@ packages:
resolve-protobuf-schema@2.1.0:
resolution: {integrity: sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==}
- responselike@4.0.2:
- resolution: {integrity: sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==}
- engines: {node: '>=20'}
+ responselike@2.0.0:
+ resolution: {integrity: sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==}
retry@0.12.0:
resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
@@ -3215,35 +3169,29 @@ packages:
engines: {node: '>=14'}
hasBin: true
- roarr@2.15.4:
- resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==}
+ roarr@2.15.3:
+ resolution: {integrity: sha512-AEjYvmAhlyxOeB9OqPUzQCo3kuAkNfuDk/HqWbZdFsqDFpapkTjiw+p4svNEoRLvuqNTxqfL+s+gtD4eDgZ+CA==}
engines: {node: '>=8.0'}
- rolldown@1.0.3:
- resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==}
+ rolldown@1.1.5:
+ resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
- safe-array-concat@1.1.4:
- resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==}
- engines: {node: '>=0.4'}
+ rw@1.3.3:
+ resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
safe-buffer@5.1.1:
resolution: {integrity: sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==}
- safe-push-apply@1.0.0:
- resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
- engines: {node: '>= 0.4'}
-
- safe-regex-test@1.1.0:
- resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
- engines: {node: '>= 0.4'}
-
safe-regex@2.1.1:
resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==}
- sanitize-filename@1.6.4:
- resolution: {integrity: sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==}
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sanitize-filename@1.6.3:
+ resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==}
sax@1.2.4:
resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
@@ -3267,18 +3215,6 @@ packages:
set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
- set-function-length@1.2.2:
- resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
- engines: {node: '>= 0.4'}
-
- set-function-name@2.0.2:
- resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
- engines: {node: '>= 0.4'}
-
- set-proto@1.0.0:
- resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
- engines: {node: '>= 0.4'}
-
setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
@@ -3290,22 +3226,6 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
- side-channel-list@1.0.1:
- resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
- engines: {node: '>= 0.4'}
-
- side-channel-map@1.0.1:
- resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
- engines: {node: '>= 0.4'}
-
- side-channel-weakmap@1.0.2:
- resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
- engines: {node: '>= 0.4'}
-
- side-channel@1.1.1:
- resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==}
- engines: {node: '>= 0.4'}
-
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
@@ -3327,10 +3247,23 @@ packages:
resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
engines: {node: '>=18'}
+ smart-buffer@4.2.0:
+ resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
+ engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
+
smol-toml@1.7.0:
resolution: {integrity: sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==}
engines: {node: '>= 18'}
+ socks-proxy-agent@8.0.3:
+ resolution: {integrity: sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==}
+ engines: {node: '>= 14'}
+
+ socks@2.8.0:
+ resolution: {integrity: sha512-AvXLNBlmf/AN7g6ZuCRNtwbLFacfNBYvy7pchLnpJ1aqCw7FPOK0HEC/LxOZxWiJpqwnjYPxxxNxXYOgX8+3fw==}
+ engines: {node: '>= 16.0.0', npm: '>= 3.0.0'}
+ deprecated: please use 2.8.1 to fix package-lock issue
+
source-map-js@1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'}
@@ -3345,17 +3278,28 @@ packages:
source-map-support@0.5.20:
resolution: {integrity: sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==}
- source-map@0.5.0:
- resolution: {integrity: sha512-gjGnxNN0K+/Pr4Mi4fs/pOtda10dKB6Wn9QvjOrH6v5TWsI7ghHuJUHoIgyM6DkUL5kr2GtPFGererzKpMBWfA==}
- engines: {node: '>=0.10.0'}
+ source-map@0.3.0:
+ resolution: {integrity: sha512-jz8leTIGS8+qJywWiO9mKza0hJxexdeIYXhDHw9avTQcXSNAGk3hiiRMpmI2Qf9dOrZDrDpgH9VNefzuacWC9A==}
+ engines: {node: '>=0.8.0'}
source-map@0.6.0:
resolution: {integrity: sha512-mTozplhTX4tLKIHYji92OTZzVyZvi+Z1qRZDeBvQFI2XUB89wrRoj/xXad3c9NZ1GPJXXRvB+k41PQCPTMC+aA==}
engines: {node: '>=0.10.0'}
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ sprintf-js@1.1.2:
+ resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==}
+
sprintf-js@1.1.3:
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
+ ssri@13.0.1:
+ resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==}
+ engines: {node: ^20.17.0 || >=22.9.0}
+
stackback@0.0.2:
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
@@ -3366,10 +3310,6 @@ packages:
std-env@4.0.0-rc.1:
resolution: {integrity: sha512-2gE+MEGsqvDEjl7LqbrCEB3Lo6+Pmt8ULCIsutKTesFBzSuNIkiWPSy65sa7WrlLebc9LcfnS5eyj69mutUp1A==}
- stop-iteration-iterator@1.1.0:
- resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
- engines: {node: '>= 0.4'}
-
string-width@1.0.1:
resolution: {integrity: sha512-sPfd1Io/7WC2MqQA7ckl7uJRKnZsUpEJamPx+l3wcD+YGqGWDMBWXkLEQdsfrFXgw+T5hdKA0+AKTlccLCjL1Q==}
engines: {node: '>=0.10.0'}
@@ -3394,16 +3334,8 @@ packages:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
- string.prototype.trim@1.2.11:
- resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==}
- engines: {node: '>= 0.4'}
-
- string.prototype.trimend@1.0.10:
- resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==}
- engines: {node: '>= 0.4'}
-
- string.prototype.trimstart@1.0.8:
- resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ string.prototype.trim@1.1.2:
+ resolution: {integrity: sha512-IlEfUereZQqIcv/LJFNPUygFkq0HJCQMnaDr5i+zyRXpeYvF4F8J8u4UFxXICLMY+O3SEfJeaye5AO5miS6a9g==}
engines: {node: '>= 0.4'}
string_decoder@0.10.24:
@@ -3436,6 +3368,10 @@ packages:
resolution: {integrity: sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==}
engines: {node: '>=8'}
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
@@ -3443,33 +3379,26 @@ packages:
resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==}
engines: {node: ^14.18.0 || >=16.0.0}
- tagged-tag@1.0.0:
- resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
- engines: {node: '>=20'}
-
- tailwindcss@4.2.4:
- resolution: {integrity: sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==}
+ tailwindcss@4.3.3:
+ resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==}
- tailwindcss@4.3.0:
- resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==}
-
- tapable@2.3.0:
- resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
engines: {node: '>=6'}
- tar@7.5.19:
- resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==}
+ tar@7.5.21:
+ resolution: {integrity: sha512-XdhtCvlMywwxpCW8YEq3lOXBJpUPTR2OHHcwLPO3HwsJqOHa2Ok/oJ7ruGzp+JrKoRPVCzJwAdEjqLW/vNRPHA==}
engines: {node: '>=18'}
temp-file@3.4.0:
resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==}
- temp@0.9.4:
- resolution: {integrity: sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==}
- engines: {node: '>=6.0.0'}
+ temp@0.9.0:
+ resolution: {integrity: sha512-YfUhPQCJoNQE5N+FJQcdPz63O3x3sdT4Xju69Gj4iZe0lBKOtnAMi0SLj9xKhGkcGhsxThvTJ/usxtFPo438zQ==}
+ engines: {'0': node >=4.0.0}
- terser@5.48.0:
- resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==}
+ terser@5.49.0:
+ resolution: {integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==}
engines: {node: '>=10'}
hasBin: true
@@ -3508,15 +3437,15 @@ packages:
resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
engines: {node: '>=14.0.0'}
- tldts-core@7.4.5:
- resolution: {integrity: sha512-pGrwzZDvPwKe+7NNUqAunb6rqTfynr0VOUhCMdqbu5xlvNiszsAJygRzwvpVycdzejlbpY+SWJOn+s75Og7FEA==}
+ tldts-core@7.0.5:
+ resolution: {integrity: sha512-o+2zicH2+bzj5PHV5M4U8OsRBJDOO0zADiTKZQTGdHSymGQYS21+V/YNrMxAxH9SopzMEXt5JuiRoOIZT5ZU9g==}
- tldts@7.4.5:
- resolution: {integrity: sha512-RfEzKWcq5fHUOFq7J3rl3Oz6ylKGtcHqUznzj4EcXsxLSIjJcvpbXAQtWGeJQ0xKnimR5e0Cn+cn9TssfMzm+g==}
+ tldts@7.0.5:
+ resolution: {integrity: sha512-Wjn3ZQIBZd3i94Gcw1hTD+BLgrkfeU71PDw7y7QxcUDCfhM5HdlZfNDbSerP4pj+ZbujKvRrlOYNRyRmQCHo0A==}
hasBin: true
- tmp-promise@3.0.3:
- resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
+ tmp-promise@3.0.2:
+ resolution: {integrity: sha512-OyCLAKU1HzBjL6Ev3gxUeraJNlbNingmi8IrHHEsYH8LTmEuhvYfqvhn2F/je+mjf4N58UmZ96OMEy1JanSCpA==}
tmp@0.2.7:
resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==}
@@ -3534,8 +3463,8 @@ packages:
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
engines: {node: '>=20'}
- truncate-utf8-bytes@1.0.2:
- resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==}
+ truncate-utf8-bytes@1.0.0:
+ resolution: {integrity: sha512-c2+evkesk63K2oOBjmzv7h1Vk/cewG2RnYd/RRYLAAz5dUN1i5qn5UCprqiOjaIfbPoXCp+h4as2AEg5vuCDSQ==}
tslib@2.4.0:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
@@ -3551,43 +3480,15 @@ packages:
resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
engines: {node: '>=10'}
- type-fest@5.7.0:
- resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==}
- engines: {node: '>=20'}
-
- typed-array-buffer@1.0.3:
- resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
- engines: {node: '>= 0.4'}
-
- typed-array-byte-length@1.0.3:
- resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
- engines: {node: '>= 0.4'}
-
- typed-array-byte-offset@1.0.4:
- resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
- engines: {node: '>= 0.4'}
-
- typed-array-length@1.0.8:
- resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==}
- engines: {node: '>= 0.4'}
-
typescript@6.0.3:
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
engines: {node: '>=14.17'}
hasBin: true
- uint8array-extras@1.5.0:
- resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==}
- engines: {node: '>=18'}
-
- unbash@4.0.2:
- resolution: {integrity: sha512-8gwNZ29+0/3zmXw7ToIHZtg6wK37xnniRUdBt7B27xZxaxfgR5tGMaGHT0t0dLtBV9fXE7zurh0s6Z1DHVjfWg==}
+ unbash@4.0.3:
+ resolution: {integrity: sha512-3cudTErfToSc4Ggv8XGXVNVli/xHKUtUZvaY5UVwhOcUPbQGz7PeaEnT/SAVgNziZtX67KEN9swMUYkLghxA1w==}
engines: {node: '>=14'}
- unbox-primitive@1.1.0:
- resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
- engines: {node: '>= 0.4'}
-
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
@@ -3613,18 +3514,18 @@ packages:
resolution: {integrity: sha512-DVeVIWUZCAQPNzm5sB0hpsG1GygTTdBnzNtYYEpInkttx5evkyqRgZi6rTczoySqp8hO5jHVKzrH0f23X8FZLg==}
engines: {node: '>=18'}
- unzipper@0.12.5:
- resolution: {integrity: sha512-tXYOi9R57Uj/2Z25SOs5RRSzq886MBQj2gY8dPL+xl/kv6s6SvByoKfAtvfVeEuhntWDgjd2o9p2lb4TVPAz0A==}
+ unzipper@0.12.3:
+ resolution: {integrity: sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA==}
upath@2.0.1:
resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==}
engines: {node: '>=4'}
- uri-js@4.2.2:
- resolution: {integrity: sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==}
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
- utf8-byte-length@1.0.5:
- resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==}
+ utf8-byte-length@1.0.1:
+ resolution: {integrity: sha512-HnaQbDHF31QaNstT1LlO0k2OVWN3Vxy1YIs1N0KF3suDBWf3DOA9sc0aqjL7CCADv+JCJUv44eNm+glVNivIxw==}
util-deprecate@1.0.1:
resolution: {integrity: sha512-/f+A7C3gucLtZ6F6z33sFBFxIrry4KPiO4S1r9KrwNv6ABp/T+IHJzzYGRFCzs2RfgTIm8cA3TJuTdc8INlkNQ==}
@@ -3667,13 +3568,13 @@ packages:
vue: ^3.0.0
vuetify: '>=3'
- vite@8.0.16:
- resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==}
+ vite@8.1.5:
+ resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': ^20.19.0 || >=22.12.0
- '@vitejs/devtools': ^0.1.18
+ '@vitejs/devtools': ^0.3.0
esbuild: ^0.27.0 || ^0.28.0
jiti: '>=1.21.0'
less: ^4.0.0
@@ -3710,20 +3611,20 @@ packages:
yaml:
optional: true
- vitest@4.1.5:
- resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==}
+ vitest@4.1.10:
+ resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==}
engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
hasBin: true
peerDependencies:
'@edge-runtime/vm': '*'
'@opentelemetry/api': ^1.9.0
'@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
- '@vitest/browser-playwright': 4.1.5
- '@vitest/browser-preview': 4.1.5
- '@vitest/browser-webdriverio': 4.1.5
- '@vitest/coverage-istanbul': 4.1.5
- '@vitest/coverage-v8': 4.1.5
- '@vitest/ui': 4.1.5
+ '@vitest/browser-playwright': 4.1.10
+ '@vitest/browser-preview': 4.1.10
+ '@vitest/browser-webdriverio': 4.1.10
+ '@vitest/coverage-istanbul': 4.1.10
+ '@vitest/coverage-v8': 4.1.10
+ '@vitest/ui': 4.1.10
happy-dom: '*'
jsdom: '*'
vite: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -3751,11 +3652,11 @@ packages:
jsdom:
optional: true
- vscode-uri@3.1.0:
- resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+ vscode-uri@3.0.8:
+ resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
- vue-component-type-helpers@3.3.5:
- resolution: {integrity: sha512-Fe1jyPJoUGpJOYKOri44jduR7My4yYINOMJISuMAbmrs+L5LbIDUc8NTWZYY3EJLK0yPLuCmcd5zoCsE4k2/KA==}
+ vue-component-type-helpers@3.0.0:
+ resolution: {integrity: sha512-J1HtqhZIqmYoNg4SLcYVFdCdsVUkMo4Z6/Wx4sQMfY8TFIIqDmd3mS2whfBIKzAA7dHMexarwYbvtB/fOUuEsw==}
vue-eslint-parser@10.4.1:
resolution: {integrity: sha512-Gk6gRDj0n/fkRa3C3l0bBheoBckUq/Rs0F/TvMWIS6nzzx67amAViMe9CkNgsP2tXyQONvGiHQESHwFtZ3aYDA==}
@@ -3763,8 +3664,8 @@ packages:
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
- vue-i18n@11.4.6:
- resolution: {integrity: sha512-l0gE7Rfy0phCa5ChKYkOq543Wgd39BCK6hkktfr1Ed4D99oRkgPK9ffShASZdeC8OJxGfdWmpYoAaAH6iLEuIg==}
+ vue-i18n@11.4.7:
+ resolution: {integrity: sha512-j6RyshdPPzqLiMAUpnpvZGFPM+rRoWi14Sl5yTsquvoW0/56DWyvhAj2o9TO2YXGvb6teg8T0xrYO9jR3urvdw==}
engines: {node: '>= 22'}
peerDependencies:
vue: ^3.0.0
@@ -3774,22 +3675,22 @@ packages:
peerDependencies:
vue: ^3.5.0
- vue-tsc@3.3.6:
- resolution: {integrity: sha512-ERXGgbKSBGFUkavrJ1Iwj0ZVxKqB/5UOx65IXy7fPf2UsoI21n3ssQLwdvx8xyUGgJe9PvSZTM/FYzIdwUDPFA==}
+ vue-tsc@3.3.7:
+ resolution: {integrity: sha512-+C+rgD49wAQ5bUTl2sp5a8Bzg4YoldMNXM+g7CFe604MYcQ8PrZPMQhIjJSzKXtPBCa+C5ayMipqjbA7splekQ==}
hasBin: true
peerDependencies:
typescript: '>=5.0.0'
- vue@3.5.39:
- resolution: {integrity: sha512-xmZCYabFGcirU8r0fTuvl/LICc1OU620rnqepaJDL/a141ZigkG7AyaxQLdqJ02ZRYzWe6YPaDHeQx7MfknQfA==}
+ vue@3.5.40:
+ resolution: {integrity: sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig==}
peerDependencies:
typescript: '*'
peerDependenciesMeta:
typescript:
optional: true
- vuetify@3.12.8:
- resolution: {integrity: sha512-gvmOWeLd6CG7LVh2Qonft5YrIb7MpbKZglRjg0ItEloSbu6hfUII2RZmRSVxGKYdiXTf7J1hI3BRSHr8J9LkxA==}
+ vuetify@3.12.10:
+ resolution: {integrity: sha512-6u/sWsIoUqWap3FxaIXZ/JObN/E2Z/BWAaliHh3GEl3Ik162zkpcIFYYLTnQiJ8PcyvMSFr+Lzyz7VoERob7JA==}
peerDependencies:
typescript: '>=4.7'
vite-plugin-vuetify: '>=2.1.0'
@@ -3829,34 +3730,19 @@ packages:
resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
- which-boxed-primitive@1.1.0:
- resolution: {integrity: sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==}
- engines: {node: '>= 0.4'}
-
- which-boxed-primitive@1.1.1:
- resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
- engines: {node: '>= 0.4'}
-
- which-builtin-type@1.2.1:
- resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
- engines: {node: '>= 0.4'}
-
- which-collection@1.0.2:
- resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
- engines: {node: '>= 0.4'}
-
which-module@2.0.0:
resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==}
- which-typed-array@1.1.22:
- resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==}
- engines: {node: '>= 0.4'}
-
which@2.0.1:
resolution: {integrity: sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==}
engines: {node: '>= 8'}
hasBin: true
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
which@5.0.0:
resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
engines: {node: ^18.17.0 || >=20.5.0}
@@ -3872,6 +3758,10 @@ packages:
engines: {node: '>=8'}
hasBin: true
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
wrap-ansi@2.0.0:
resolution: {integrity: sha512-HG2fWLyu61fAbCvUqlNNF2lOEk+WZDsUkAn5+dEtAF7dl89viTM5DyS/OxWROhJN8bRkPHT3/ww8XQVu9UQwrw==}
engines: {node: '>=0.10.0'}
@@ -3888,12 +3778,8 @@ packages:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
- wrappy@1.0.2:
- resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
-
- xml-name-validator@4.0.0:
- resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
- engines: {node: '>=12'}
+ wrappy@1.0.0:
+ resolution: {integrity: sha512-3y8PlpIQuv3/g9rN12ffm6FrWo+fCLlt8mtAruKSVgnPgdV4SoxOF8qmJ+6BXjsfDFe/EMUGFWjCu2dImNHjBQ==}
xml-name-validator@5.0.0:
resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
@@ -3913,18 +3799,10 @@ packages:
resolution: {integrity: sha512-kRIX84vvgLbzkKD1wMkCxTtNwrBFfgXIzGuFKFBISJRDcMz4N8FBllDEndJkNl6HrFuQuzSbAEWpnuY/ydPVXg==}
engines: {node: '>=0.4'}
- xtend@4.0.2:
- resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
- engines: {node: '>=0.4'}
-
y18n@5.0.5:
resolution: {integrity: sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==}
engines: {node: '>=10'}
- y18n@5.0.8:
- resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
- engines: {node: '>=10'}
-
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
@@ -3941,10 +3819,6 @@ packages:
resolution: {integrity: sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==}
engines: {node: '>=6'}
- yargs-parser@22.0.0:
- resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
- engines: {node: ^20.19.0 || ^22.12.0 || >=23}
-
yargs@12.0.5:
resolution: {integrity: sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==}
@@ -3952,8 +3826,8 @@ packages:
resolution: {integrity: sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==}
engines: {node: '>=8'}
- yargs@17.7.3:
- resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==}
+ yargs@17.6.2:
+ resolution: {integrity: sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==}
engines: {node: '>=12'}
yocto-queue@0.1.0:
@@ -3971,8 +3845,6 @@ packages:
snapshots:
- '@aashutoshrathi/word-wrap@1.2.6': {}
-
'@asamuzakjp/css-color@5.1.11':
dependencies:
'@asamuzakjp/generational-cache': 1.0.1
@@ -4043,7 +3915,7 @@ snapshots:
dependencies:
'@csstools/css-tokenizer': 4.0.0
- '@csstools/css-syntax-patches-for-csstree@1.1.6(css-tree@3.2.1)':
+ '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)':
optionalDependencies:
css-tree: 3.2.1
@@ -4051,9 +3923,9 @@ snapshots:
'@egjs/hammerjs@2.0.17':
dependencies:
- '@types/hammerjs': 2.0.36
+ '@types/hammerjs': 2.0.46
- '@electron-internal/extract-zip@1.0.4': {}
+ '@electron-internal/extract-zip@1.0.1': {}
'@electron/asar@3.4.1':
dependencies:
@@ -4072,7 +3944,7 @@ snapshots:
debug: 4.3.1
env-paths: 2.2.0
fs-extra: 8.1.0
- got: 15.0.7
+ got: 11.8.6
progress: 2.0.3
semver: 7.5.2
sumchecker: 3.0.1
@@ -4100,13 +3972,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@electron/rebuild@4.0.6':
+ '@electron/rebuild@4.0.4':
dependencies:
'@malept/cross-spawn-promise': 2.0.0
debug: 4.3.1
- node-abi: 4.31.0
+ node-abi: 4.2.0
node-api-version: 0.2.1
- node-gyp: 12.4.0
+ node-gyp: 12.2.0
read-binary-file-arch: 1.0.6
transitivePeerDependencies:
- supports-color
@@ -4134,31 +4006,15 @@ snapshots:
- supports-color
optional: true
- '@emnapi/core@1.10.0':
- dependencies:
- '@emnapi/wasi-threads': 1.2.1
- tslib: 2.4.0
- optional: true
-
- '@emnapi/core@1.11.0':
- dependencies:
- '@emnapi/wasi-threads': 1.2.2
- tslib: 2.4.0
- optional: true
-
'@emnapi/core@1.11.1':
dependencies:
'@emnapi/wasi-threads': 1.2.2
tslib: 2.4.0
optional: true
- '@emnapi/runtime@1.10.0':
- dependencies:
- tslib: 2.4.0
- optional: true
-
- '@emnapi/runtime@1.11.0':
+ '@emnapi/core@1.11.2':
dependencies:
+ '@emnapi/wasi-threads': 1.2.2
tslib: 2.4.0
optional: true
@@ -4167,7 +4023,7 @@ snapshots:
tslib: 2.4.0
optional: true
- '@emnapi/wasi-threads@1.2.1':
+ '@emnapi/runtime@1.11.2':
dependencies:
tslib: 2.4.0
optional: true
@@ -4179,23 +4035,23 @@ snapshots:
'@epic-web/invariant@1.0.0': {}
- '@eslint-community/eslint-utils@4.4.0(eslint@9.39.4(jiti@2.7.0))':
+ '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0))':
dependencies:
- eslint: 9.39.4(jiti@2.7.0)
- eslint-visitor-keys: 3.3.0
+ eslint: 9.39.5(jiti@2.7.0)
+ eslint-visitor-keys: 3.4.3
- '@eslint-community/eslint-utils@4.8.0(eslint@9.39.4(jiti@2.7.0))':
+ '@eslint-community/eslint-utils@4.8.0(eslint@9.39.5(jiti@2.7.0))':
dependencies:
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.5(jiti@2.7.0)
eslint-visitor-keys: 3.4.3
- '@eslint-community/regexpp@4.12.1': {}
+ '@eslint-community/regexpp@4.12.2': {}
'@eslint/config-array@0.21.2':
dependencies:
'@eslint/object-schema': 2.1.7
- debug: 4.3.1
- minimatch: 10.2.3
+ debug: 4.4.3
+ minimatch: 10.2.5
transitivePeerDependencies:
- supports-color
@@ -4207,21 +4063,21 @@ snapshots:
dependencies:
'@types/json-schema': 7.0.15
- '@eslint/eslintrc@3.3.5':
+ '@eslint/eslintrc@3.3.6':
dependencies:
ajv: 6.14.0
- debug: 4.3.1
+ debug: 4.4.3
espree: 10.4.0
globals: 14.0.0
- ignore: 5.2.0
- import-fresh: 3.2.1
- js-yaml: 4.2.0
- minimatch: 10.2.3
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 5.2.1
+ minimatch: 10.2.5
strip-json-comments: 3.1.1
transitivePeerDependencies:
- supports-color
- '@eslint/js@9.39.4': {}
+ '@eslint/js@9.39.5': {}
'@eslint/object-schema@2.1.7': {}
@@ -4234,38 +4090,43 @@ snapshots:
optionalDependencies:
'@noble/hashes': 2.2.0
- '@fontsource/noto-sans@5.2.10': {}
+ '@fontsource/noto-sans@5.3.0': {}
- '@humanfs/core@0.19.1': {}
+ '@gar/promise-retry@1.0.3': {}
- '@humanfs/node@0.16.6':
+ '@humanfs/core@0.19.2':
dependencies:
- '@humanfs/core': 0.19.1
- '@humanwhocodes/retry': 0.3.0
+ '@humanfs/types': 0.15.0
- '@humanwhocodes/module-importer@1.0.1': {}
+ '@humanfs/node@0.16.8':
+ dependencies:
+ '@humanfs/core': 0.19.2
+ '@humanfs/types': 0.15.0
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanfs/types@0.15.0': {}
- '@humanwhocodes/retry@0.3.0': {}
+ '@humanwhocodes/module-importer@1.0.1': {}
- '@humanwhocodes/retry@0.4.2': {}
+ '@humanwhocodes/retry@0.4.3': {}
- '@intlify/core-base@11.4.6':
+ '@intlify/core-base@11.4.7':
dependencies:
- '@intlify/devtools-types': 11.4.6
- '@intlify/message-compiler': 11.4.6
- '@intlify/shared': 11.4.6
+ '@intlify/devtools-types': 11.4.7
+ '@intlify/message-compiler': 11.4.7
+ '@intlify/shared': 11.4.7
- '@intlify/devtools-types@11.4.6':
+ '@intlify/devtools-types@11.4.7':
dependencies:
- '@intlify/core-base': 11.4.6
- '@intlify/shared': 11.4.6
+ '@intlify/core-base': 11.4.7
+ '@intlify/shared': 11.4.7
- '@intlify/message-compiler@11.4.6':
+ '@intlify/message-compiler@11.4.7':
dependencies:
- '@intlify/shared': 11.4.6
+ '@intlify/shared': 11.4.7
source-map-js: 1.0.2
- '@intlify/shared@11.4.6': {}
+ '@intlify/shared@11.4.7': {}
'@isaacs/cliui@8.0.2':
dependencies:
@@ -4276,8 +4137,6 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
- '@isaacs/cliui@9.0.0': {}
-
'@isaacs/fs-minipass@4.0.0':
dependencies:
minipass: 7.1.2
@@ -4288,16 +4147,15 @@ snapshots:
'@jridgewell/sourcemap-codec': 1.4.10
'@jridgewell/trace-mapping': 0.3.9
- '@jridgewell/gen-mapping@0.3.5':
+ '@jridgewell/gen-mapping@0.3.13':
dependencies:
- '@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.4.10
- '@jridgewell/trace-mapping': 0.3.24
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
'@jridgewell/remapping@2.3.5':
dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.24
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
'@jridgewell/resolve-uri@3.0.3': {}
@@ -4316,11 +4174,6 @@ snapshots:
'@jridgewell/sourcemap-codec@1.5.5': {}
- '@jridgewell/trace-mapping@0.3.24':
- dependencies:
- '@jridgewell/resolve-uri': 3.1.0
- '@jridgewell/sourcemap-codec': 1.4.14
-
'@jridgewell/trace-mapping@0.3.31':
dependencies:
'@jridgewell/resolve-uri': 3.1.0
@@ -4331,8 +4184,6 @@ snapshots:
'@jridgewell/resolve-uri': 3.0.3
'@jridgewell/sourcemap-codec': 1.4.10
- '@keyv/serialize@1.1.1': {}
-
'@malept/cross-spawn-promise@2.0.0':
dependencies:
cross-spawn: 7.0.5
@@ -4342,184 +4193,194 @@ snapshots:
debug: 4.3.1
fs-extra: 9.0.0
lodash: 4.18.0
- tmp-promise: 3.0.3
+ tmp-promise: 3.0.2
transitivePeerDependencies:
- supports-color
- '@mapbox/jsonlint-lines-primitives@2.0.3': {}
+ '@mapbox/jsonlint-lines-primitives@2.0.2': {}
- '@mapbox/unitbezier@1.0.0': {}
+ '@mapbox/unitbezier@0.0.1': {}
- '@maplibre/maplibre-gl-style-spec@24.10.0':
+ '@maplibre/maplibre-gl-style-spec@24.4.1':
dependencies:
- '@mapbox/jsonlint-lines-primitives': 2.0.3
- '@mapbox/unitbezier': 1.0.0
+ '@mapbox/jsonlint-lines-primitives': 2.0.2
+ '@mapbox/unitbezier': 0.0.1
json-stringify-pretty-compact: 4.0.0
minimist: 1.2.8
quickselect: 3.0.0
+ rw: 1.3.3
tinyqueue: 3.0.0
'@mdi/font@7.4.47': {}
'@mdi/js@7.4.47': {}
- '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
+ '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
dependencies:
- '@emnapi/core': 1.10.0
- '@emnapi/runtime': 1.10.0
- '@tybys/wasm-util': 0.10.1
- optional: true
-
- '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)':
- dependencies:
- '@emnapi/core': 1.11.0
- '@emnapi/runtime': 1.11.0
- '@tybys/wasm-util': 0.10.2
+ '@emnapi/core': 1.11.1
+ '@emnapi/runtime': 1.11.1
+ '@tybys/wasm-util': 0.10.3
optional: true
- '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)':
+ '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)':
dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@tybys/wasm-util': 0.10.2
+ '@emnapi/core': 1.11.2
+ '@emnapi/runtime': 1.11.2
+ '@tybys/wasm-util': 0.10.3
optional: true
'@noble/hashes@1.4.0': {}
'@noble/hashes@2.2.0': {}
+ '@npmcli/agent@4.0.2':
+ dependencies:
+ agent-base: 7.1.0
+ http-proxy-agent: 7.0.0
+ https-proxy-agent: 7.0.1
+ lru-cache: 11.2.1
+ socks-proxy-agent: 8.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@npmcli/fs@5.0.0':
+ dependencies:
+ semver: 7.5.2
+
+ '@npmcli/redact@4.0.0': {}
+
'@one-ini/wasm@0.1.1': {}
- '@oxc-parser/binding-android-arm-eabi@0.137.0':
+ '@oxc-parser/binding-android-arm-eabi@0.140.0':
optional: true
- '@oxc-parser/binding-android-arm64@0.137.0':
+ '@oxc-parser/binding-android-arm64@0.140.0':
optional: true
- '@oxc-parser/binding-darwin-arm64@0.137.0':
+ '@oxc-parser/binding-darwin-arm64@0.140.0':
optional: true
- '@oxc-parser/binding-darwin-x64@0.137.0':
+ '@oxc-parser/binding-darwin-x64@0.140.0':
optional: true
- '@oxc-parser/binding-freebsd-x64@0.137.0':
+ '@oxc-parser/binding-freebsd-x64@0.140.0':
optional: true
- '@oxc-parser/binding-linux-arm-gnueabihf@0.137.0':
+ '@oxc-parser/binding-linux-arm-gnueabihf@0.140.0':
optional: true
- '@oxc-parser/binding-linux-arm-musleabihf@0.137.0':
+ '@oxc-parser/binding-linux-arm-musleabihf@0.140.0':
optional: true
- '@oxc-parser/binding-linux-arm64-gnu@0.137.0':
+ '@oxc-parser/binding-linux-arm64-gnu@0.140.0':
optional: true
- '@oxc-parser/binding-linux-arm64-musl@0.137.0':
+ '@oxc-parser/binding-linux-arm64-musl@0.140.0':
optional: true
- '@oxc-parser/binding-linux-ppc64-gnu@0.137.0':
+ '@oxc-parser/binding-linux-ppc64-gnu@0.140.0':
optional: true
- '@oxc-parser/binding-linux-riscv64-gnu@0.137.0':
+ '@oxc-parser/binding-linux-riscv64-gnu@0.140.0':
optional: true
- '@oxc-parser/binding-linux-riscv64-musl@0.137.0':
+ '@oxc-parser/binding-linux-riscv64-musl@0.140.0':
optional: true
- '@oxc-parser/binding-linux-s390x-gnu@0.137.0':
+ '@oxc-parser/binding-linux-s390x-gnu@0.140.0':
optional: true
- '@oxc-parser/binding-linux-x64-gnu@0.137.0':
+ '@oxc-parser/binding-linux-x64-gnu@0.140.0':
optional: true
- '@oxc-parser/binding-linux-x64-musl@0.137.0':
+ '@oxc-parser/binding-linux-x64-musl@0.140.0':
optional: true
- '@oxc-parser/binding-openharmony-arm64@0.137.0':
+ '@oxc-parser/binding-openharmony-arm64@0.140.0':
optional: true
- '@oxc-parser/binding-wasm32-wasi@0.137.0':
+ '@oxc-parser/binding-wasm32-wasi@0.140.0':
dependencies:
- '@emnapi/core': 1.11.1
- '@emnapi/runtime': 1.11.1
- '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
+ '@emnapi/core': 1.11.2
+ '@emnapi/runtime': 1.11.2
+ '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
optional: true
- '@oxc-parser/binding-win32-arm64-msvc@0.137.0':
+ '@oxc-parser/binding-win32-arm64-msvc@0.140.0':
optional: true
- '@oxc-parser/binding-win32-ia32-msvc@0.137.0':
+ '@oxc-parser/binding-win32-ia32-msvc@0.140.0':
optional: true
- '@oxc-parser/binding-win32-x64-msvc@0.137.0':
+ '@oxc-parser/binding-win32-x64-msvc@0.140.0':
optional: true
- '@oxc-project/types@0.133.0': {}
+ '@oxc-project/types@0.139.0': {}
- '@oxc-project/types@0.137.0': {}
+ '@oxc-project/types@0.140.0': {}
- '@oxc-resolver/binding-android-arm-eabi@11.21.3':
+ '@oxc-resolver/binding-android-arm-eabi@11.24.2':
optional: true
- '@oxc-resolver/binding-android-arm64@11.21.3':
+ '@oxc-resolver/binding-android-arm64@11.24.2':
optional: true
- '@oxc-resolver/binding-darwin-arm64@11.21.3':
+ '@oxc-resolver/binding-darwin-arm64@11.24.2':
optional: true
- '@oxc-resolver/binding-darwin-x64@11.21.3':
+ '@oxc-resolver/binding-darwin-x64@11.24.2':
optional: true
- '@oxc-resolver/binding-freebsd-x64@11.21.3':
+ '@oxc-resolver/binding-freebsd-x64@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-arm-gnueabihf@11.21.3':
+ '@oxc-resolver/binding-linux-arm-gnueabihf@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-arm-musleabihf@11.21.3':
+ '@oxc-resolver/binding-linux-arm-musleabihf@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-arm64-gnu@11.21.3':
+ '@oxc-resolver/binding-linux-arm64-gnu@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-arm64-musl@11.21.3':
+ '@oxc-resolver/binding-linux-arm64-musl@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-ppc64-gnu@11.21.3':
+ '@oxc-resolver/binding-linux-ppc64-gnu@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-riscv64-gnu@11.21.3':
+ '@oxc-resolver/binding-linux-riscv64-gnu@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-riscv64-musl@11.21.3':
+ '@oxc-resolver/binding-linux-riscv64-musl@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-s390x-gnu@11.21.3':
+ '@oxc-resolver/binding-linux-s390x-gnu@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-x64-gnu@11.21.3':
+ '@oxc-resolver/binding-linux-x64-gnu@11.24.2':
optional: true
- '@oxc-resolver/binding-linux-x64-musl@11.21.3':
+ '@oxc-resolver/binding-linux-x64-musl@11.24.2':
optional: true
- '@oxc-resolver/binding-openharmony-arm64@11.21.3':
+ '@oxc-resolver/binding-openharmony-arm64@11.24.2':
optional: true
- '@oxc-resolver/binding-wasm32-wasi@11.21.3':
+ '@oxc-resolver/binding-wasm32-wasi@11.24.2':
dependencies:
- '@emnapi/core': 1.11.0
- '@emnapi/runtime': 1.11.0
- '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)
+ '@emnapi/core': 1.11.2
+ '@emnapi/runtime': 1.11.2
+ '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)
optional: true
- '@oxc-resolver/binding-win32-arm64-msvc@11.21.3':
+ '@oxc-resolver/binding-win32-arm64-msvc@11.24.2':
optional: true
- '@oxc-resolver/binding-win32-x64-msvc@11.21.3':
+ '@oxc-resolver/binding-win32-x64-msvc@11.24.2':
optional: true
- '@peculiar/asn1-schema@2.8.0':
+ '@peculiar/asn1-schema@2.7.0':
dependencies:
'@peculiar/utils': 2.0.3
asn1js: 3.0.10
@@ -4527,7 +4388,7 @@ snapshots:
'@peculiar/json-schema@1.1.12':
dependencies:
- tslib: 2.4.0
+ tslib: 2.8.1
'@peculiar/utils@2.0.3':
dependencies:
@@ -4535,7 +4396,7 @@ snapshots:
'@peculiar/webcrypto@1.7.1':
dependencies:
- '@peculiar/asn1-schema': 2.8.0
+ '@peculiar/asn1-schema': 2.7.0
'@peculiar/json-schema': 1.1.12
'@peculiar/utils': 2.0.3
tslib: 2.8.1
@@ -4554,180 +4415,185 @@ snapshots:
'@polka/url@1.0.0-next.24': {}
- '@rolldown/binding-android-arm64@1.0.3':
+ '@rolldown/binding-android-arm64@1.1.5':
optional: true
- '@rolldown/binding-darwin-arm64@1.0.3':
+ '@rolldown/binding-darwin-arm64@1.1.5':
optional: true
- '@rolldown/binding-darwin-x64@1.0.3':
+ '@rolldown/binding-darwin-x64@1.1.5':
optional: true
- '@rolldown/binding-freebsd-x64@1.0.3':
+ '@rolldown/binding-freebsd-x64@1.1.5':
optional: true
- '@rolldown/binding-linux-arm-gnueabihf@1.0.3':
+ '@rolldown/binding-linux-arm-gnueabihf@1.1.5':
optional: true
- '@rolldown/binding-linux-arm64-gnu@1.0.3':
+ '@rolldown/binding-linux-arm64-gnu@1.1.5':
optional: true
- '@rolldown/binding-linux-arm64-musl@1.0.3':
+ '@rolldown/binding-linux-arm64-musl@1.1.5':
optional: true
- '@rolldown/binding-linux-ppc64-gnu@1.0.3':
+ '@rolldown/binding-linux-ppc64-gnu@1.1.5':
optional: true
- '@rolldown/binding-linux-s390x-gnu@1.0.3':
+ '@rolldown/binding-linux-s390x-gnu@1.1.5':
optional: true
- '@rolldown/binding-linux-x64-gnu@1.0.3':
+ '@rolldown/binding-linux-x64-gnu@1.1.5':
optional: true
- '@rolldown/binding-linux-x64-musl@1.0.3':
+ '@rolldown/binding-linux-x64-musl@1.1.5':
optional: true
- '@rolldown/binding-openharmony-arm64@1.0.3':
+ '@rolldown/binding-openharmony-arm64@1.1.5':
optional: true
- '@rolldown/binding-wasm32-wasi@1.0.3':
+ '@rolldown/binding-wasm32-wasi@1.1.5':
dependencies:
- '@emnapi/core': 1.10.0
- '@emnapi/runtime': 1.10.0
- '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ '@emnapi/core': 1.11.1
+ '@emnapi/runtime': 1.11.1
+ '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)
optional: true
- '@rolldown/binding-win32-arm64-msvc@1.0.3':
+ '@rolldown/binding-win32-arm64-msvc@1.1.5':
optional: true
- '@rolldown/binding-win32-x64-msvc@1.0.3':
+ '@rolldown/binding-win32-x64-msvc@1.1.5':
optional: true
'@rolldown/pluginutils@1.0.0': {}
'@rolldown/pluginutils@1.0.1': {}
- '@sec-ant/readable-stream@0.4.1': {}
-
- '@sindresorhus/is@8.1.0': {}
+ '@sindresorhus/is@4.0.0': {}
'@standard-schema/spec@1.1.0': {}
- '@tailwindcss/forms@0.5.11(tailwindcss@4.3.0)':
+ '@szmarczak/http-timer@4.0.5':
+ dependencies:
+ defer-to-connect: 2.0.0
+
+ '@tailwindcss/forms@0.5.11(tailwindcss@4.3.3)':
dependencies:
mini-svg-data-uri: 1.2.3
- tailwindcss: 4.3.0
+ tailwindcss: 4.3.3
- '@tailwindcss/node@4.2.4':
+ '@tailwindcss/node@4.3.3':
dependencies:
'@jridgewell/remapping': 2.3.5
- enhanced-resolve: 5.19.0
- jiti: 2.6.1
+ enhanced-resolve: 5.24.1
+ jiti: 2.7.0
lightningcss: 1.32.0
magic-string: 0.30.21
source-map-js: 1.2.1
- tailwindcss: 4.2.4
+ tailwindcss: 4.3.3
- '@tailwindcss/oxide-android-arm64@4.2.4':
+ '@tailwindcss/oxide-android-arm64@4.3.3':
optional: true
- '@tailwindcss/oxide-darwin-arm64@4.2.4':
+ '@tailwindcss/oxide-darwin-arm64@4.3.3':
optional: true
- '@tailwindcss/oxide-darwin-x64@4.2.4':
+ '@tailwindcss/oxide-darwin-x64@4.3.3':
optional: true
- '@tailwindcss/oxide-freebsd-x64@4.2.4':
+ '@tailwindcss/oxide-freebsd-x64@4.3.3':
optional: true
- '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.4':
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3':
optional: true
- '@tailwindcss/oxide-linux-arm64-gnu@4.2.4':
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.3':
optional: true
- '@tailwindcss/oxide-linux-arm64-musl@4.2.4':
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.3':
optional: true
- '@tailwindcss/oxide-linux-x64-gnu@4.2.4':
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.3':
optional: true
- '@tailwindcss/oxide-linux-x64-musl@4.2.4':
+ '@tailwindcss/oxide-linux-x64-musl@4.3.3':
optional: true
- '@tailwindcss/oxide-wasm32-wasi@4.2.4':
+ '@tailwindcss/oxide-wasm32-wasi@4.3.3':
optional: true
- '@tailwindcss/oxide-win32-arm64-msvc@4.2.4':
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.3':
optional: true
- '@tailwindcss/oxide-win32-x64-msvc@4.2.4':
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.3':
optional: true
- '@tailwindcss/oxide@4.2.4':
+ '@tailwindcss/oxide@4.3.3':
optionalDependencies:
- '@tailwindcss/oxide-android-arm64': 4.2.4
- '@tailwindcss/oxide-darwin-arm64': 4.2.4
- '@tailwindcss/oxide-darwin-x64': 4.2.4
- '@tailwindcss/oxide-freebsd-x64': 4.2.4
- '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.4
- '@tailwindcss/oxide-linux-arm64-gnu': 4.2.4
- '@tailwindcss/oxide-linux-arm64-musl': 4.2.4
- '@tailwindcss/oxide-linux-x64-gnu': 4.2.4
- '@tailwindcss/oxide-linux-x64-musl': 4.2.4
- '@tailwindcss/oxide-wasm32-wasi': 4.2.4
- '@tailwindcss/oxide-win32-arm64-msvc': 4.2.4
- '@tailwindcss/oxide-win32-x64-msvc': 4.2.4
+ '@tailwindcss/oxide-android-arm64': 4.3.3
+ '@tailwindcss/oxide-darwin-arm64': 4.3.3
+ '@tailwindcss/oxide-darwin-x64': 4.3.3
+ '@tailwindcss/oxide-freebsd-x64': 4.3.3
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.3
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.3.3
+ '@tailwindcss/oxide-linux-arm64-musl': 4.3.3
+ '@tailwindcss/oxide-linux-x64-gnu': 4.3.3
+ '@tailwindcss/oxide-linux-x64-musl': 4.3.3
+ '@tailwindcss/oxide-wasm32-wasi': 4.3.3
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3
+ '@tailwindcss/oxide-win32-x64-msvc': 4.3.3
- '@tailwindcss/vite@4.2.4(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))':
+ '@tailwindcss/vite@4.3.3(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))':
dependencies:
- '@tailwindcss/node': 4.2.4
- '@tailwindcss/oxide': 4.2.4
- tailwindcss: 4.2.4
- vite: 8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)
+ '@tailwindcss/node': 4.3.3
+ '@tailwindcss/oxide': 4.3.3
+ tailwindcss: 4.3.3
+ vite: 8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)
- '@tanstack/virtual-core@3.17.2': {}
+ '@tanstack/virtual-core@3.17.6': {}
- '@tanstack/vue-virtual@3.13.30(vue@3.5.39(typescript@6.0.3))':
+ '@tanstack/vue-virtual@3.13.34(vue@3.5.40(typescript@6.0.3))':
dependencies:
- '@tanstack/virtual-core': 3.17.2
- vue: 3.5.39(typescript@6.0.3)
+ '@tanstack/virtual-core': 3.17.6
+ vue: 3.5.40(typescript@6.0.3)
- '@tybys/wasm-util@0.10.1':
+ '@tybys/wasm-util@0.10.3':
dependencies:
tslib: 2.4.0
optional: true
- '@tybys/wasm-util@0.10.2':
+ '@types/cacheable-request@6.0.1':
dependencies:
- tslib: 2.4.0
- optional: true
+ '@types/http-cache-semantics': 4.0.0
+ '@types/keyv': 3.0.0
+ '@types/node': 24.9.0
+ '@types/responselike': 1.0.0
- '@types/chai@5.2.2':
+ '@types/chai@5.2.3':
dependencies:
- '@types/deep-eql': 4.0.0
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
- '@types/debug@4.1.13':
- dependencies:
- '@types/ms': 2.1.0
+ '@types/debug@4.1.6': {}
- '@types/deep-eql@4.0.0': {}
+ '@types/deep-eql@4.0.2': {}
'@types/estree@1.0.0': {}
- '@types/estree@1.0.6': {}
+ '@types/estree@1.0.9': {}
'@types/fs-extra@9.0.13':
dependencies:
'@types/node': 0.0.0
- '@types/hammerjs@2.0.36': {}
+ '@types/hammerjs@2.0.46': {}
- '@types/http-cache-semantics@4.2.0': {}
+ '@types/http-cache-semantics@4.0.0': {}
'@types/json-schema@7.0.15': {}
- '@types/ms@2.1.0': {}
+ '@types/keyv@3.0.0':
+ dependencies:
+ '@types/node': 24.9.0
'@types/node@0.0.0': {}
@@ -4737,19 +4603,23 @@ snapshots:
'@types/rbush@4.0.0': {}
+ '@types/responselike@1.0.0':
+ dependencies:
+ '@types/node': 24.9.0
+
'@types/trusted-types@2.0.7':
optional: true
- '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))':
+ '@vitejs/plugin-vue@6.0.8(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3))':
dependencies:
'@rolldown/pluginutils': 1.0.1
- vite: 8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)
- vue: 3.5.39(typescript@6.0.3)
+ vite: 8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)
+ vue: 3.5.40(typescript@6.0.3)
- '@vitest/coverage-v8@4.1.5(vitest@4.1.5)':
+ '@vitest/coverage-v8@4.1.10(vitest@4.1.10)':
dependencies:
'@bcoe/v8-coverage': 1.0.2
- '@vitest/utils': 4.1.5
+ '@vitest/utils': 4.1.10
ast-v8-to-istanbul: 1.0.0
istanbul-lib-coverage: 3.2.2
istanbul-lib-report: 3.0.1
@@ -4758,67 +4628,57 @@ snapshots:
obug: 2.1.1
std-env: 4.0.0-rc.1
tinyrainbow: 3.1.0
- vitest: 4.1.5(@types/node@24.9.0)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.9)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))
+ vitest: 4.1.10(@types/node@24.9.0)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))
- '@vitest/expect@4.1.5':
+ '@vitest/expect@4.1.10':
dependencies:
'@standard-schema/spec': 1.1.0
- '@types/chai': 5.2.2
- '@vitest/spy': 4.1.5
- '@vitest/utils': 4.1.5
+ '@types/chai': 5.2.3
+ '@vitest/spy': 4.1.10
+ '@vitest/utils': 4.1.10
chai: 6.2.2
tinyrainbow: 3.1.0
- '@vitest/mocker@4.1.5(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))':
+ '@vitest/mocker@4.1.10(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))':
dependencies:
- '@vitest/spy': 4.1.5
+ '@vitest/spy': 4.1.10
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)
+ vite: 8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)
- '@vitest/pretty-format@4.1.5':
+ '@vitest/pretty-format@4.1.10':
dependencies:
tinyrainbow: 3.1.0
- '@vitest/pretty-format@4.1.9':
+ '@vitest/runner@4.1.10':
dependencies:
- tinyrainbow: 3.1.0
-
- '@vitest/runner@4.1.5':
- dependencies:
- '@vitest/utils': 4.1.5
+ '@vitest/utils': 4.1.10
pathe: 2.0.3
- '@vitest/snapshot@4.1.5':
+ '@vitest/snapshot@4.1.10':
dependencies:
- '@vitest/pretty-format': 4.1.5
- '@vitest/utils': 4.1.5
+ '@vitest/pretty-format': 4.1.10
+ '@vitest/utils': 4.1.10
magic-string: 0.30.21
pathe: 2.0.3
- '@vitest/spy@4.1.5': {}
+ '@vitest/spy@4.1.10': {}
- '@vitest/ui@4.1.9(vitest@4.1.5)':
+ '@vitest/ui@4.1.10(vitest@4.1.10)':
dependencies:
- '@vitest/utils': 4.1.9
- fflate: 0.8.2
+ '@vitest/utils': 4.1.10
+ fflate: 0.8.3
flatted: 3.4.2
pathe: 2.0.3
sirv: 3.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.1.0
- vitest: 4.1.5(@types/node@24.9.0)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.9)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))
-
- '@vitest/utils@4.1.5':
- dependencies:
- '@vitest/pretty-format': 4.1.5
- convert-source-map: 2.0.0
- tinyrainbow: 3.1.0
+ vitest: 4.1.10(@types/node@24.9.0)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))
- '@vitest/utils@4.1.9':
+ '@vitest/utils@4.1.10':
dependencies:
- '@vitest/pretty-format': 4.1.9
+ '@vitest/pretty-format': 4.1.10
convert-source-map: 2.0.0
tinyrainbow: 3.1.0
@@ -4832,95 +4692,97 @@ snapshots:
dependencies:
'@volar/language-core': 2.4.28
path-browserify: 1.0.1
- vscode-uri: 3.1.0
+ vscode-uri: 3.0.8
- '@vue/compiler-core@3.5.39':
+ '@vue/compiler-core@3.5.40':
dependencies:
'@babel/parser': 7.29.7
- '@vue/shared': 3.5.39
+ '@vue/shared': 3.5.40
entities: 7.0.1
estree-walker: 2.0.2
source-map-js: 1.2.1
- '@vue/compiler-dom@3.5.39':
+ '@vue/compiler-dom@3.5.40':
dependencies:
- '@vue/compiler-core': 3.5.39
- '@vue/shared': 3.5.39
+ '@vue/compiler-core': 3.5.40
+ '@vue/shared': 3.5.40
- '@vue/compiler-sfc@3.5.39':
+ '@vue/compiler-sfc@3.5.40':
dependencies:
'@babel/parser': 7.29.7
- '@vue/compiler-core': 3.5.39
- '@vue/compiler-dom': 3.5.39
- '@vue/compiler-ssr': 3.5.39
- '@vue/shared': 3.5.39
+ '@vue/compiler-core': 3.5.40
+ '@vue/compiler-dom': 3.5.40
+ '@vue/compiler-ssr': 3.5.40
+ '@vue/shared': 3.5.40
estree-walker: 2.0.2
magic-string: 0.30.21
- postcss: 8.5.15
+ postcss: 8.5.19
source-map-js: 1.2.1
- '@vue/compiler-ssr@3.5.39':
+ '@vue/compiler-ssr@3.5.40':
dependencies:
- '@vue/compiler-dom': 3.5.39
- '@vue/shared': 3.5.39
+ '@vue/compiler-dom': 3.5.40
+ '@vue/shared': 3.5.40
+
+ '@vue/devtools-api@6.5.0': {}
'@vue/devtools-api@6.6.4': {}
- '@vue/language-core@3.3.6':
+ '@vue/language-core@3.3.7':
dependencies:
'@volar/language-core': 2.4.28
- '@vue/compiler-dom': 3.5.39
- '@vue/shared': 3.5.39
+ '@vue/compiler-dom': 3.5.40
+ '@vue/shared': 3.5.40
alien-signals: 3.2.1
muggle-string: 0.4.1
path-browserify: 1.0.1
picomatch: 4.0.4
- '@vue/reactivity@3.5.39':
+ '@vue/reactivity@3.5.40':
dependencies:
- '@vue/shared': 3.5.39
+ '@vue/shared': 3.5.40
- '@vue/runtime-core@3.5.39':
+ '@vue/runtime-core@3.5.40':
dependencies:
- '@vue/reactivity': 3.5.39
- '@vue/shared': 3.5.39
+ '@vue/reactivity': 3.5.40
+ '@vue/shared': 3.5.40
- '@vue/runtime-dom@3.5.39':
+ '@vue/runtime-dom@3.5.40':
dependencies:
- '@vue/reactivity': 3.5.39
- '@vue/runtime-core': 3.5.39
- '@vue/shared': 3.5.39
+ '@vue/reactivity': 3.5.40
+ '@vue/runtime-core': 3.5.40
+ '@vue/shared': 3.5.40
csstype: 3.2.3
- '@vue/server-renderer@3.5.39(vue@3.5.39(typescript@6.0.3))':
+ '@vue/server-renderer@3.5.40':
dependencies:
- '@vue/compiler-ssr': 3.5.39
- '@vue/shared': 3.5.39
- vue: 3.5.39(typescript@6.0.3)
+ '@vue/compiler-ssr': 3.5.40
+ '@vue/runtime-dom': 3.5.40
+ '@vue/shared': 3.5.40
- '@vue/shared@3.5.39': {}
+ '@vue/shared@3.5.40': {}
- '@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.39)(@vue/server-renderer@3.5.39(vue@3.5.39(typescript@6.0.3)))(vue@3.5.39(typescript@6.0.3))':
+ '@vue/test-utils@2.4.11(@vue/compiler-dom@3.5.40)(@vue/server-renderer@3.5.40)(vue@3.5.40(typescript@6.0.3))':
dependencies:
- '@vue/compiler-dom': 3.5.39
- js-beautify: 1.15.4
- vue: 3.5.39(typescript@6.0.3)
- vue-component-type-helpers: 3.3.5
+ '@vue/compiler-dom': 3.5.40
+ js-beautify: 1.14.9
+ vue: 3.5.40(typescript@6.0.3)
+ vue-component-type-helpers: 3.0.0
optionalDependencies:
- '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@6.0.3))
+ '@vue/server-renderer': 3.5.40
- '@vuetify/loader-shared@2.1.2(vue@3.5.39(typescript@6.0.3))(vuetify@3.12.8)':
+ '@vuetify/loader-shared@2.1.2(vue@3.5.40(typescript@6.0.3))(vuetify@3.12.10)':
dependencies:
upath: 2.0.1
- vue: 3.5.39(typescript@6.0.3)
- vuetify: 3.12.8(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.39(typescript@6.0.3))
+ vue: 3.5.40(typescript@6.0.3)
+ vuetify: 3.12.10(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.40(typescript@6.0.3))
'@zarrita/storage@0.2.0':
dependencies:
reference-spec-reader: 0.2.0
unzipit: 2.0.0
- abbrev@2.0.0: {}
+ abbrev@1.0.3: {}
abbrev@4.0.0: {}
@@ -4928,9 +4790,9 @@ snapshots:
dependencies:
acorn: 8.14.0
- acorn-jsx@5.3.2(acorn@8.15.0):
+ acorn-jsx@5.3.2(acorn@8.17.0):
dependencies:
- acorn: 8.15.0
+ acorn: 8.17.0
acorn@0.11.0: {}
@@ -4938,6 +4800,8 @@ snapshots:
acorn@8.15.0: {}
+ acorn@8.17.0: {}
+
agent-base@7.0.2:
dependencies:
debug: 4.3.1
@@ -4950,22 +4814,30 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ agent-base@7.1.1:
+ dependencies:
+ debug: 4.3.1
+ transitivePeerDependencies:
+ - supports-color
+
ajv@6.14.0:
dependencies:
fast-deep-equal: 3.1.3
- fast-json-stable-stringify: 2.0.0
+ fast-json-stable-stringify: 2.1.0
json-schema-traverse: 0.4.1
- uri-js: 4.2.2
+ uri-js: 4.4.1
- ajv@8.20.0:
+ ajv@8.18.0:
dependencies:
fast-deep-equal: 3.1.3
- fast-uri: 3.1.2
+ fast-uri: 4.1.1
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
alien-signals@3.2.1: {}
+ amdefine@1.0.1: {}
+
ansi-regex@5.0.1: {}
ansi-styles@4.0.0:
@@ -4976,6 +4848,10 @@ snapshots:
dependencies:
color-convert: 2.0.1
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
ansi-styles@6.1.0: {}
app-builder-lib@26.15.3(dmg-builder@26.15.3)(electron-builder-squirrel-windows@26.15.3):
@@ -4985,13 +4861,13 @@ snapshots:
'@electron/get': 3.1.0
'@electron/notarize': 2.5.0
'@electron/osx-sign': 1.3.3
- '@electron/rebuild': 4.0.6
+ '@electron/rebuild': 4.0.4
'@electron/universal': 2.0.3
'@malept/flatpak-bundler': 0.4.0
'@noble/hashes': 2.2.0
'@peculiar/webcrypto': 1.7.1
'@types/fs-extra': 9.0.13
- ajv: 8.20.0
+ ajv: 8.18.0
asn1js: 3.0.10
async-exit-hook: 2.0.1
builder-util: 26.15.3
@@ -5001,13 +4877,13 @@ snapshots:
debug: 4.3.1
dmg-builder: 26.15.3(electron-builder-squirrel-windows@26.15.3)
dotenv: 16.6.1
- dotenv-expand: 11.0.7
+ dotenv-expand: 11.0.6
ejs: 3.1.10
electron-builder-squirrel-windows: 26.15.3(dmg-builder@26.15.3)
electron-publish: 26.15.3
fs-extra: 10.1.0
- hosted-git-info: 10.1.1
- isbinaryfile: 5.0.7
+ hosted-git-info: 2.8.9
+ isbinaryfile: 5.0.0
jiti: 2.4.2
js-yaml: 4.2.0
json5: 2.2.3
@@ -5016,40 +4892,27 @@ snapshots:
pkijs: 3.4.0
plist: 3.0.5
proper-lockfile: 4.1.2
- resedit: 1.7.2
+ resedit: 1.7.0
semver: 7.5.2
- tar: 7.5.19
+ tar: 7.5.21
temp-file: 3.4.0
tiny-async-pool: 1.3.0
- unzipper: 0.12.5
+ unzipper: 0.12.3
which: 5.0.0
transitivePeerDependencies:
- supports-color
argparse@2.0.1: {}
- array-buffer-byte-length@1.0.2:
- dependencies:
- call-bound: 1.0.4
- is-array-buffer: 3.0.5
-
- arraybuffer.prototype.slice@1.0.4:
- dependencies:
- array-buffer-byte-length: 1.0.2
- call-bind: 1.0.9
- define-properties: 1.2.1
- es-abstract: 1.24.2
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
- is-array-buffer: 3.0.5
-
asn1js@3.0.10:
dependencies:
pvtsutils: 1.3.6
pvutils: 1.1.5
tslib: 2.8.1
- ast-types@0.8.15: {}
+ assertion-error@2.0.1: {}
+
+ ast-types@0.6.12: {}
ast-v8-to-istanbul@1.0.0:
dependencies:
@@ -5059,25 +4922,17 @@ snapshots:
async-exit-hook@2.0.1: {}
- async-function@1.0.0: {}
-
- async@3.2.6: {}
+ async@3.2.3: {}
asynckit@0.4.0: {}
at-least-node@1.0.0: {}
- available-typed-arrays@1.0.7:
- dependencies:
- possible-typed-array-names: 1.1.0
-
aws4@1.13.2: {}
balanced-match@1.0.0: {}
- balanced-match@4.0.2:
- dependencies:
- jackspeak: 4.2.3
+ balanced-match@4.0.4: {}
base64-js@1.5.1: {}
@@ -5091,7 +4946,7 @@ snapshots:
boolbase@1.0.0: {}
- boolean@3.2.0:
+ boolean@3.0.1:
optional: true
brace-expansion@1.1.12:
@@ -5099,9 +4954,14 @@ snapshots:
balanced-match: 1.0.0
concat-map: 0.0.1
- brace-expansion@5.0.7:
+ brace-expansion@1.1.13:
dependencies:
- balanced-match: 4.0.2
+ balanced-match: 1.0.0
+ concat-map: 0.0.1
+
+ brace-expansion@5.0.7:
+ dependencies:
+ balanced-match: 4.0.4
buffer-from@1.0.0: {}
@@ -5114,7 +4974,7 @@ snapshots:
builder-util@26.15.3:
dependencies:
- '@types/debug': 4.1.13
+ '@types/debug': 4.1.6
builder-util-runtime: 9.7.0
chalk: 4.1.2
cross-spawn: 7.0.5
@@ -5123,7 +4983,7 @@ snapshots:
http-proxy-agent: 7.0.0
https-proxy-agent: 7.0.0
js-yaml: 4.2.0
- sanitize-filename: 1.6.4
+ sanitize-filename: 1.6.3
source-map-support: 0.5.19
stat-mode: 1.0.0
temp-file: 3.4.0
@@ -5131,60 +4991,44 @@ snapshots:
transitivePeerDependencies:
- supports-color
- byte-counter@0.1.0: {}
-
bytestreamjs@2.0.1: {}
- cacheable-lookup@7.0.0: {}
-
- cacheable-request@13.0.19:
+ cacache@20.0.4:
dependencies:
- '@types/http-cache-semantics': 4.2.0
- get-stream: 9.0.1
- http-cache-semantics: 4.1.1
- keyv: 5.6.0
- mimic-response: 4.0.0
- normalize-url: 8.1.1
- responselike: 4.0.2
+ '@npmcli/fs': 5.0.0
+ fs-minipass: 3.0.3
+ glob: 10.5.0
+ lru-cache: 11.1.0
+ minipass: 7.0.3
+ minipass-collect: 2.0.1
+ minipass-flush: 1.0.7
+ minipass-pipeline: 1.2.4
+ p-map: 7.0.6
+ ssri: 13.0.1
- call-bind-apply-helpers@1.0.0:
- dependencies:
- es-errors: 1.3.0
- function-bind: 1.1.2
+ cacheable-lookup@5.0.3: {}
- call-bind-apply-helpers@1.0.1:
+ cacheable-request@7.0.2:
dependencies:
- es-errors: 1.3.0
- function-bind: 1.1.2
+ clone-response: 1.0.2
+ get-stream: 5.1.0
+ http-cache-semantics: 4.1.1
+ keyv: 4.0.0
+ lowercase-keys: 2.0.0
+ normalize-url: 6.0.1
+ responselike: 2.0.0
- call-bind-apply-helpers@1.0.2:
+ call-bind-apply-helpers@1.0.1:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
- call-bind@1.0.9:
- dependencies:
- call-bind-apply-helpers: 1.0.2
- es-define-property: 1.0.1
- get-intrinsic: 1.3.0
- set-function-length: 1.2.2
-
- call-bound@1.0.4:
- dependencies:
- call-bind-apply-helpers: 1.0.2
- get-intrinsic: 1.3.0
-
- callsites@3.0.0: {}
+ callsites@3.1.0: {}
camelcase@5.0.0: {}
chai@6.2.2: {}
- chalk@4.0.0:
- dependencies:
- ansi-styles: 4.1.0
- supports-color: 7.1.0
-
chalk@4.1.1:
dependencies:
ansi-styles: 4.1.0
@@ -5192,19 +5036,17 @@ snapshots:
chalk@4.1.2:
dependencies:
- ansi-styles: 4.1.0
- supports-color: 7.1.0
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
chownr@3.0.0: {}
chromium-pickle-js@0.2.0: {}
- chunk-data@0.1.0: {}
+ ci-info@4.2.0: {}
ci-info@4.3.1: {}
- ci-info@4.4.0: {}
-
cli-table3@0.5.0:
dependencies:
object-assign: 4.1.0
@@ -5230,6 +5072,10 @@ snapshots:
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
+ clone-response@1.0.2:
+ dependencies:
+ mimic-response: 1.0.0
+
code-point-at@1.0.0:
dependencies:
number-is-nan: 1.0.0
@@ -5250,7 +5096,7 @@ snapshots:
dependencies:
delayed-stream: 1.0.0
- commander@10.0.0: {}
+ commander@11.0.0: {}
commander@2.20.0: {}
@@ -5273,7 +5119,7 @@ snapshots:
config-chain@1.1.13:
dependencies:
ini: 1.3.6
- proto-list: 1.2.4
+ proto-list: 1.2.1
convert-source-map@2.0.0: {}
@@ -5295,9 +5141,9 @@ snapshots:
cross-spawn@7.0.6:
dependencies:
- path-key: 3.1.0
+ path-key: 3.1.1
shebang-command: 2.0.0
- which: 2.0.1
+ which: 2.0.2
css-tree@3.2.1:
dependencies:
@@ -5315,57 +5161,43 @@ snapshots:
transitivePeerDependencies:
- '@noble/hashes'
- data-view-buffer@1.0.2:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-data-view: 1.0.2
-
- data-view-byte-length@1.0.2:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-data-view: 1.0.2
-
- data-view-byte-offset@1.0.1:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-data-view: 1.0.2
-
dayjs@1.11.21: {}
debug@4.3.1:
dependencies:
ms: 2.0.0
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
decamelize@1.2.0: {}
decimal.js@10.6.0: {}
- decompress-response@10.0.0:
+ decompress-response@6.0.0:
dependencies:
- mimic-response: 4.0.0
+ mimic-response: 3.1.0
+
+ deep-is@0.1.4: {}
- deep-is@0.1.3: {}
+ defer-to-connect@2.0.0: {}
- define-data-property@1.1.4:
+ define-properties@1.1.2:
dependencies:
- es-define-property: 1.0.1
- es-errors: 1.3.0
- gopd: 1.2.0
+ foreach: 2.0.6
+ object-keys: 1.0.8
- define-properties@1.2.1:
+ define-properties@1.1.3:
dependencies:
- define-data-property: 1.1.4
- has-property-descriptors: 1.0.2
- object-keys: 1.1.1
+ object-keys: 1.0.12
+ optional: true
delayed-stream@1.0.0: {}
detect-libc@2.0.3: {}
- detect-node@2.1.0:
+ detect-node@2.0.4:
optional: true
dijkstrajs@1.0.1: {}
@@ -5385,23 +5217,17 @@ snapshots:
- electron-builder-squirrel-windows
- supports-color
- dompurify@3.4.11:
+ dompurify@3.4.12:
optionalDependencies:
'@types/trusted-types': 2.0.7
- dotenv-expand@11.0.7:
+ dotenv-expand@11.0.6:
dependencies:
dotenv: 16.6.1
dotenv@16.6.1: {}
dunder-proto@1.0.0:
- dependencies:
- call-bind-apply-helpers: 1.0.0
- es-errors: 1.3.0
- gopd: 1.2.0
-
- dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.1
es-errors: 1.3.0
@@ -5411,14 +5237,14 @@ snapshots:
dependencies:
readable-stream: 2.0.2
- earcut@3.1.0: {}
+ earcut@3.0.0: {}
eastasianwidth@0.2.0: {}
- editorconfig@1.0.7:
+ editorconfig@1.0.3:
dependencies:
'@one-ini/wasm': 0.1.1
- commander: 10.0.0
+ commander: 11.0.0
minimatch: 10.2.3
semver: 7.5.2
@@ -5441,12 +5267,12 @@ snapshots:
builder-util: 26.15.3
builder-util-runtime: 9.7.0
chalk: 4.1.2
- ci-info: 4.4.0
+ ci-info: 4.2.0
dmg-builder: 26.15.3(electron-builder-squirrel-windows@26.15.3)
fs-extra: 10.1.0
lazy-val: 1.0.5
simple-update-notifier: 2.0.0
- yargs: 17.7.3
+ yargs: 17.6.2
transitivePeerDependencies:
- electron-builder-squirrel-windows
- supports-color
@@ -5463,7 +5289,7 @@ snapshots:
form-data: 4.0.6
fs-extra: 10.1.0
lazy-val: 1.0.5
- mime: 2.6.0
+ mime: 2.5.2
transitivePeerDependencies:
- supports-color
@@ -5473,7 +5299,7 @@ snapshots:
debug: 4.3.1
fs-extra: 7.0.1
lodash: 4.18.1
- temp: 0.9.4
+ temp: 0.9.0
optionalDependencies:
'@electron/windows-sign': 1.1.2
transitivePeerDependencies:
@@ -5481,7 +5307,7 @@ snapshots:
electron@42.4.0:
dependencies:
- '@electron-internal/extract-zip': 1.0.4
+ '@electron-internal/extract-zip': 1.0.1
'@electron/get': 3.1.0
'@types/node': 24.9.0
transitivePeerDependencies:
@@ -5495,10 +5321,14 @@ snapshots:
emoji-regex@9.2.2: {}
- enhanced-resolve@5.19.0:
+ end-of-stream@1.1.0:
dependencies:
- graceful-fs: 4.2.4
- tapable: 2.3.0
+ once: 1.3.1
+
+ enhanced-resolve@5.24.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.3
entities@7.0.1: {}
@@ -5506,86 +5336,25 @@ snapshots:
env-paths@2.2.0: {}
- err-code@2.0.3: {}
+ err-code@2.0.2: {}
- es-abstract-get@1.0.0:
+ es-abstract@1.5.0:
dependencies:
- es-errors: 1.3.0
- es-object-atoms: 1.1.2
- is-callable: 1.2.7
- object-inspect: 1.13.4
-
- es-abstract@1.24.2:
- dependencies:
- array-buffer-byte-length: 1.0.2
- arraybuffer.prototype.slice: 1.0.4
- available-typed-arrays: 1.0.7
- call-bind: 1.0.9
- call-bound: 1.0.4
- data-view-buffer: 1.0.2
- data-view-byte-length: 1.0.2
- data-view-byte-offset: 1.0.1
- es-define-property: 1.0.1
- es-errors: 1.3.0
- es-object-atoms: 1.1.2
- es-set-tostringtag: 2.1.0
- es-to-primitive: 1.3.4
- function.prototype.name: 1.2.0
- get-intrinsic: 1.3.0
- get-proto: 1.0.1
- get-symbol-description: 1.1.0
- globalthis: 1.0.4
- gopd: 1.2.0
- has-property-descriptors: 1.0.2
- has-proto: 1.2.0
- has-symbols: 1.1.0
- hasown: 2.0.2
- internal-slot: 1.1.0
- is-array-buffer: 3.0.5
- is-callable: 1.2.7
- is-data-view: 1.0.2
- is-negative-zero: 2.0.3
- is-regex: 1.2.1
- is-set: 2.0.3
- is-shared-array-buffer: 1.0.4
- is-string: 1.1.1
- is-typed-array: 1.1.15
- is-weakref: 1.1.1
- math-intrinsics: 1.1.0
- object-inspect: 1.13.4
- object-keys: 1.1.1
- object.assign: 4.1.7
- own-keys: 1.0.1
- regexp.prototype.flags: 1.5.4
- safe-array-concat: 1.1.4
- safe-push-apply: 1.0.0
- safe-regex-test: 1.1.0
- set-proto: 1.0.0
- stop-iteration-iterator: 1.1.0
- string.prototype.trim: 1.2.11
- string.prototype.trimend: 1.0.10
- string.prototype.trimstart: 1.0.8
- typed-array-buffer: 1.0.3
- typed-array-byte-length: 1.0.3
- typed-array-byte-offset: 1.0.4
- typed-array-length: 1.0.8
- unbox-primitive: 1.1.0
- which-typed-array: 1.1.22
+ es-to-primitive: 1.1.0
+ function-bind: 1.0.2
+ is-callable: 1.1.1
+ is-regex: 1.0.3
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
- es-module-lexer@2.2.0: {}
+ es-module-lexer@2.0.0: {}
es-object-atoms@1.0.0:
dependencies:
es-errors: 1.3.0
- es-object-atoms@1.1.2:
- dependencies:
- es-errors: 1.3.0
-
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
@@ -5593,14 +5362,11 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.4
- es-to-primitive@1.3.4:
+ es-to-primitive@1.1.0:
dependencies:
- es-abstract-get: 1.0.0
- es-define-property: 1.0.1
- es-errors: 1.3.0
- is-callable: 1.2.7
- is-date-object: 1.1.0
- is-symbol: 1.1.1
+ is-callable: 1.1.1
+ is-date-object: 1.0.1
+ is-symbol: 1.0.1
es6-error@4.1.1:
optional: true
@@ -5609,33 +5375,33 @@ snapshots:
escape-string-regexp@4.0.0: {}
- eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)):
+ eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)):
dependencies:
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.5(jiti@2.7.0)
- eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.9.3):
+ eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6):
dependencies:
- eslint: 9.39.4(jiti@2.7.0)
- prettier: 3.9.3
+ eslint: 9.39.5(jiti@2.7.0)
+ prettier: 3.9.6
prettier-linter-helpers: 1.0.1
synckit: 0.11.13
optionalDependencies:
- eslint-config-prettier: 10.1.8(eslint@9.39.4(jiti@2.7.0))
+ eslint-config-prettier: 10.1.8(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-security@3.0.1:
dependencies:
safe-regex: 2.1.1
- eslint-plugin-vue@10.9.2(eslint@9.39.4(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0))):
+ eslint-plugin-vue@10.10.0(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))):
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@9.39.4(jiti@2.7.0))
- eslint: 9.39.4(jiti@2.7.0)
+ '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0))
+ eslint: 9.39.5(jiti@2.7.0)
natural-compare: 1.4.0
nth-check: 2.1.1
- postcss-selector-parser: 7.1.0
+ postcss-selector-parser: 7.1.4
semver: 7.5.2
- vue-eslint-parser: 10.4.1(eslint@9.39.4(jiti@2.7.0))
- xml-name-validator: 4.0.0
+ vue-eslint-parser: 10.4.1(eslint@9.39.5(jiti@2.7.0))
+ xml-name-validator: 5.0.0
eslint-scope@8.2.0:
dependencies:
@@ -5645,9 +5411,7 @@ snapshots:
eslint-scope@8.4.0:
dependencies:
esrecurse: 4.3.0
- estraverse: 5.2.0
-
- eslint-visitor-keys@3.3.0: {}
+ estraverse: 5.3.0
eslint-visitor-keys@3.4.3: {}
@@ -5655,42 +5419,42 @@ snapshots:
eslint-visitor-keys@4.2.1: {}
- eslint@9.39.4(jiti@2.7.0):
+ eslint@9.39.5(jiti@2.7.0):
dependencies:
- '@eslint-community/eslint-utils': 4.8.0(eslint@9.39.4(jiti@2.7.0))
- '@eslint-community/regexpp': 4.12.1
+ '@eslint-community/eslint-utils': 4.8.0(eslint@9.39.5(jiti@2.7.0))
+ '@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.21.2
'@eslint/config-helpers': 0.4.2
'@eslint/core': 0.17.0
- '@eslint/eslintrc': 3.3.5
- '@eslint/js': 9.39.4
+ '@eslint/eslintrc': 3.3.6
+ '@eslint/js': 9.39.5
'@eslint/plugin-kit': 0.4.1
- '@humanfs/node': 0.16.6
+ '@humanfs/node': 0.16.8
'@humanwhocodes/module-importer': 1.0.1
- '@humanwhocodes/retry': 0.4.2
- '@types/estree': 1.0.6
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.9
ajv: 6.14.0
- chalk: 4.0.0
+ chalk: 4.1.2
cross-spawn: 7.0.6
- debug: 4.3.1
+ debug: 4.4.3
escape-string-regexp: 4.0.0
eslint-scope: 8.4.0
eslint-visitor-keys: 4.2.1
espree: 10.4.0
- esquery: 1.5.0
- esutils: 2.0.2
+ esquery: 1.7.0
+ esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 8.0.0
find-up: 5.0.0
glob-parent: 6.0.2
- ignore: 5.2.0
+ ignore: 5.3.2
imurmurhash: 0.1.4
- is-glob: 4.0.0
+ is-glob: 4.0.3
json-stable-stringify-without-jsonify: 1.0.1
lodash.merge: 4.6.2
- minimatch: 10.2.3
+ minimatch: 10.2.5
natural-compare: 1.4.0
- optionator: 0.9.3
+ optionator: 0.9.4
optionalDependencies:
jiti: 2.7.0
transitivePeerDependencies:
@@ -5706,23 +5470,23 @@ snapshots:
espree@10.4.0:
dependencies:
- acorn: 8.15.0
- acorn-jsx: 5.3.2(acorn@8.15.0)
+ acorn: 8.17.0
+ acorn-jsx: 5.3.2(acorn@8.17.0)
eslint-visitor-keys: 4.2.1
- esprima-fb@15001.1001.0-dev-harmony-fb: {}
+ esprima-fb@12001.1.0-dev-harmony-fb: {}
- esquery@1.5.0:
+ esquery@1.6.0:
dependencies:
estraverse: 5.1.0
- esquery@1.6.0:
+ esquery@1.7.0:
dependencies:
estraverse: 5.3.0
esrecurse@4.3.0:
dependencies:
- estraverse: 5.2.0
+ estraverse: 5.3.0
estraverse@5.1.0: {}
@@ -5736,14 +5500,14 @@ snapshots:
dependencies:
'@types/estree': 1.0.0
- esutils@2.0.2: {}
+ esutils@2.0.3: {}
execa@0.10.0:
dependencies:
cross-spawn: 7.0.5
get-stream: 3.0.0
is-stream: 1.1.0
- npm-run-path: 2.0.2
+ npm-run-path: 2.0.0
p-finally: 1.0.0
signal-exit: 3.0.0
strip-eof: 1.0.0
@@ -5760,13 +5524,13 @@ snapshots:
fast-deep-equal@3.1.3: {}
- fast-diff@1.3.0: {}
+ fast-diff@1.1.2: {}
- fast-json-stable-stringify@2.0.0: {}
+ fast-json-stable-stringify@2.1.0: {}
fast-levenshtein@2.0.6: {}
- fast-uri@3.1.2: {}
+ fast-uri@4.1.1: {}
fd-package-json@2.0.0:
dependencies:
@@ -5782,11 +5546,11 @@ snapshots:
fflate@0.8.0: {}
- fflate@0.8.2: {}
+ fflate@0.8.3: {}
file-entry-cache@8.0.0:
dependencies:
- flat-cache: 4.0.0
+ flat-cache: 4.0.1
filelist@1.0.1:
dependencies:
@@ -5806,17 +5570,18 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
- flat-cache@4.0.0:
+ flat-cache@4.0.1:
dependencies:
flatted: 3.4.2
keyv: 4.5.4
- rimraf: 5.0.5
flatted@3.4.2: {}
- for-each@0.3.5:
+ for-each@0.3.3:
dependencies:
- is-callable: 1.2.7
+ is-callable: 1.1.3
+
+ foreach@2.0.6: {}
foreground-child@3.1.0:
dependencies:
@@ -5847,7 +5612,7 @@ snapshots:
jsonfile: 6.0.1
universalify: 2.0.0
- fs-extra@11.3.1:
+ fs-extra@11.2.0:
dependencies:
graceful-fs: 4.2.2
jsonfile: 6.0.1
@@ -5879,6 +5644,10 @@ snapshots:
jsonfile: 6.0.1
universalify: 1.0.0
+ fs-minipass@3.0.3:
+ dependencies:
+ minipass: 7.0.3
+
fs.realpath@1.0.0: {}
fsevents@2.3.2:
@@ -5887,26 +5656,10 @@ snapshots:
fsevents@2.3.3:
optional: true
- function-bind@1.1.1: {}
+ function-bind@1.0.2: {}
function-bind@1.1.2: {}
- function.prototype.name@1.2.0:
- dependencies:
- call-bind: 1.0.9
- call-bound: 1.0.4
- es-define-property: 1.0.1
- es-errors: 1.3.0
- functions-have-names: 1.2.3
- has-property-descriptors: 1.0.2
- hasown: 2.0.4
- is-callable: 1.2.7
- is-document.all: 1.0.0
-
- functions-have-names@1.2.3: {}
-
- generator-function@2.0.0: {}
-
geotiff@3.0.5:
dependencies:
'@petamoriken/float16': 3.9.3
@@ -5924,16 +5677,10 @@ snapshots:
get-caller-file@2.0.5: {}
- get-intrinsic@1.1.3:
- dependencies:
- function-bind: 1.1.1
- has: 1.0.3
- has-symbols: 1.0.3
-
get-intrinsic@1.2.6:
dependencies:
call-bind-apply-helpers: 1.0.1
- dunder-proto: 1.0.1
+ dunder-proto: 1.0.0
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.0.0
@@ -5943,36 +5690,11 @@ snapshots:
hasown: 2.0.4
math-intrinsics: 1.0.0
- get-intrinsic@1.3.0:
- dependencies:
- call-bind-apply-helpers: 1.0.2
- es-define-property: 1.0.1
- es-errors: 1.3.0
- es-object-atoms: 1.1.2
- function-bind: 1.1.2
- get-proto: 1.0.1
- gopd: 1.2.0
- has-symbols: 1.1.0
- hasown: 2.0.2
- math-intrinsics: 1.1.0
-
- get-proto@1.0.1:
- dependencies:
- dunder-proto: 1.0.1
- es-object-atoms: 1.1.2
-
get-stream@3.0.0: {}
- get-stream@9.0.1:
- dependencies:
- '@sec-ant/readable-stream': 0.4.1
- is-stream: 4.0.1
-
- get-symbol-description@1.1.0:
+ get-stream@5.1.0:
dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- get-intrinsic: 1.3.0
+ pump: 3.0.0
get-tsconfig@4.14.0:
dependencies:
@@ -5997,48 +5719,43 @@ snapshots:
inflight: 1.0.4
inherits: 2.0.0
minimatch: 10.2.3
- once: 1.4.0
+ once: 1.3.0
path-is-absolute: 1.0.0
global-agent@3.0.0:
dependencies:
- boolean: 3.2.0
+ boolean: 3.0.1
es6-error: 4.1.1
matcher: 3.0.0
- roarr: 2.15.4
+ roarr: 2.15.3
semver: 7.5.2
serialize-error: 7.0.1
optional: true
globals@14.0.0: {}
- globals@17.6.0: {}
+ globals@17.7.0: {}
- globalthis@1.0.4:
+ globalthis@1.0.1:
dependencies:
- define-properties: 1.2.1
- gopd: 1.0.1
-
- gopd@1.0.1:
- dependencies:
- get-intrinsic: 1.1.3
+ define-properties: 1.1.3
+ optional: true
gopd@1.2.0: {}
- got@15.0.7:
- dependencies:
- '@sindresorhus/is': 8.1.0
- byte-counter: 0.1.0
- cacheable-lookup: 7.0.0
- cacheable-request: 13.0.19
- chunk-data: 0.1.0
- decompress-response: 10.0.0
- http2-wrapper: 2.2.1
- keyv: 5.6.0
- lowercase-keys: 4.0.1
- responselike: 4.0.2
- type-fest: 5.7.0
- uint8array-extras: 1.5.0
+ got@11.8.6:
+ dependencies:
+ '@sindresorhus/is': 4.0.0
+ '@szmarczak/http-timer': 4.0.5
+ '@types/cacheable-request': 6.0.1
+ '@types/responselike': 1.0.0
+ cacheable-lookup: 5.0.3
+ cacheable-request: 7.0.2
+ decompress-response: 6.0.0
+ http2-wrapper: 1.0.0-beta.5.2
+ lowercase-keys: 2.0.0
+ p-cancelable: 2.0.0
+ responselike: 2.0.0
graceful-fs@4.1.2: {}
@@ -6047,24 +5764,16 @@ snapshots:
graceful-fs@4.2.0: {}
+ graceful-fs@4.2.11: {}
+
graceful-fs@4.2.2: {}
graceful-fs@4.2.4: {}
graceful-fs@4.2.6: {}
- has-bigints@1.1.0: {}
-
has-flag@4.0.0: {}
- has-property-descriptors@1.0.2:
- dependencies:
- es-define-property: 1.0.1
-
- has-proto@1.2.0:
- dependencies:
- dunder-proto: 1.0.0
-
has-symbols@1.0.3: {}
has-symbols@1.1.0: {}
@@ -6073,21 +5782,11 @@ snapshots:
dependencies:
has-symbols: 1.0.3
- has@1.0.3:
- dependencies:
- function-bind: 1.1.1
-
- hasown@2.0.2:
- dependencies:
- function-bind: 1.1.2
-
hasown@2.0.4:
dependencies:
function-bind: 1.1.2
- hosted-git-info@10.1.1:
- dependencies:
- lru-cache: 11.1.0
+ hosted-git-info@2.8.9: {}
html-encoding-sniffer@6.0.0(@noble/hashes@2.2.0):
dependencies:
@@ -6106,10 +5805,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
- http2-wrapper@2.2.1:
+ http2-wrapper@1.0.0-beta.5.2:
dependencies:
quick-lru: 5.1.1
- resolve-alpn: 1.2.1
+ resolve-alpn: 1.0.0
https-proxy-agent@7.0.0:
dependencies:
@@ -6118,24 +5817,36 @@ snapshots:
transitivePeerDependencies:
- supports-color
- ignore@5.2.0: {}
+ https-proxy-agent@7.0.1:
+ dependencies:
+ agent-base: 7.1.0
+ debug: 4.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ iconv-lite@0.7.2:
+ dependencies:
+ safer-buffer: 2.1.2
+ optional: true
+
+ ignore@5.3.2: {}
immediate@3.0.5:
dependencies:
inline-process-browser: 1.0.0
unreachable-branch-transform: 0.3.0
- import-fresh@3.2.1:
+ import-fresh@3.3.1:
dependencies:
- parent-module: 1.0.0
+ parent-module: 1.0.1
resolve-from: 4.0.0
imurmurhash@0.1.4: {}
inflight@1.0.4:
dependencies:
- once: 1.4.0
- wrappy: 1.0.2
+ once: 1.3.0
+ wrappy: 1.0.0
inherits@2.0.0: {}
@@ -6150,67 +5861,23 @@ snapshots:
falafel: 1.0.1
through2: 0.6.5
- internal-slot@1.1.0:
- dependencies:
- es-errors: 1.3.0
- hasown: 2.0.2
- side-channel: 1.1.1
-
invert-kv@2.0.0: {}
- is-array-buffer@3.0.5:
- dependencies:
- call-bind: 1.0.9
- call-bound: 1.0.4
- get-intrinsic: 1.3.0
-
- is-async-function@2.1.1:
+ ip-address@9.0.5:
dependencies:
- async-function: 1.0.0
- call-bound: 1.0.4
- get-proto: 1.0.1
- has-tostringtag: 1.0.2
- safe-regex-test: 1.1.0
-
- is-bigint@1.1.0:
- dependencies:
- has-bigints: 1.1.0
+ jsbn: 1.1.0
+ sprintf-js: 1.1.3
is-blob@2.1.0: {}
- is-boolean-object@1.2.0:
- dependencies:
- call-bind: 1.0.9
- has-tostringtag: 1.0.2
-
- is-boolean-object@1.2.2:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
- is-callable@1.2.7: {}
+ is-callable@1.1.1: {}
- is-data-view@1.0.2:
- dependencies:
- call-bound: 1.0.4
- get-intrinsic: 1.3.0
- is-typed-array: 1.1.15
+ is-callable@1.1.3: {}
- is-date-object@1.1.0:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
- is-document.all@1.0.0:
- dependencies:
- call-bound: 1.0.4
+ is-date-object@1.0.1: {}
is-extglob@2.1.1: {}
- is-finalizationregistry@1.1.1:
- dependencies:
- call-bound: 1.0.4
-
is-fullwidth-code-point@1.0.0:
dependencies:
number-is-nan: 1.0.0
@@ -6219,101 +5886,32 @@ snapshots:
is-fullwidth-code-point@3.0.0: {}
- is-generator-function@1.1.2:
- dependencies:
- call-bound: 1.0.4
- generator-function: 2.0.0
- get-proto: 1.0.1
- has-tostringtag: 1.0.2
- safe-regex-test: 1.1.0
-
- is-glob@4.0.0:
- dependencies:
- is-extglob: 2.1.1
-
is-glob@4.0.3:
dependencies:
is-extglob: 2.1.1
- is-map@2.0.3: {}
-
- is-negative-zero@2.0.3: {}
-
- is-number-object@1.1.0:
- dependencies:
- call-bind: 1.0.9
- has-tostringtag: 1.0.2
-
- is-number-object@1.1.1:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
is-potential-custom-element-name@1.0.1: {}
- is-regex@1.2.1:
- dependencies:
- call-bound: 1.0.4
- gopd: 1.2.0
- has-tostringtag: 1.0.2
- hasown: 2.0.2
-
- is-set@2.0.3: {}
-
- is-shared-array-buffer@1.0.4:
- dependencies:
- call-bound: 1.0.4
+ is-regex@1.0.3: {}
is-stream@1.1.0: {}
- is-stream@4.0.1: {}
-
- is-string@1.1.1:
- dependencies:
- call-bound: 1.0.4
- has-tostringtag: 1.0.2
-
- is-symbol@1.1.0:
- dependencies:
- call-bind: 1.0.9
- has-symbols: 1.1.0
- safe-regex-test: 1.1.0
-
- is-symbol@1.1.1:
- dependencies:
- call-bound: 1.0.4
- has-symbols: 1.1.0
- safe-regex-test: 1.1.0
-
- is-typed-array@1.1.15:
- dependencies:
- which-typed-array: 1.1.22
-
- is-weakmap@2.0.2: {}
-
- is-weakref@1.1.1:
- dependencies:
- call-bound: 1.0.4
-
- is-weakset@2.0.4:
- dependencies:
- call-bound: 1.0.4
- get-intrinsic: 1.2.6
+ is-symbol@1.0.1: {}
isarray@0.0.1: {}
isarray@1.0.0: {}
- isarray@2.0.5: {}
-
isbinaryfile@4.0.8: {}
- isbinaryfile@5.0.7: {}
+ isbinaryfile@5.0.0: {}
isexe@2.0.0: {}
isexe@3.1.1: {}
+ isexe@3.1.5: {}
+
istanbul-lib-coverage@3.2.2: {}
istanbul-lib-report@3.0.1:
@@ -6333,32 +5931,23 @@ snapshots:
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
- jackspeak@4.2.3:
- dependencies:
- '@isaacs/cliui': 9.0.0
-
jake@10.8.5:
dependencies:
- async: 3.2.6
- chalk: 4.1.1
+ async: 3.2.3
+ chalk: 4.1.2
filelist: 1.0.1
minimatch: 10.2.3
jiti@2.4.2: {}
- jiti@2.6.1: {}
-
jiti@2.7.0: {}
- js-beautify@1.15.4:
+ js-beautify@1.14.9:
dependencies:
config-chain: 1.1.13
- editorconfig: 1.0.7
+ editorconfig: 1.0.3
glob: 10.5.0
- js-cookie: 3.0.8
- nopt: 7.2.1
-
- js-cookie@3.0.8: {}
+ nopt: 6.0.0
js-tokens@10.0.0: {}
@@ -6366,12 +5955,18 @@ snapshots:
dependencies:
argparse: 2.0.1
+ js-yaml@5.2.1:
+ dependencies:
+ argparse: 2.0.1
+
+ jsbn@1.1.0: {}
+
jsdom@29.1.1(@noble/hashes@2.2.0):
dependencies:
'@asamuzakjp/css-color': 5.1.11
'@asamuzakjp/dom-selector': 7.1.1
'@bramus/specificity': 2.4.2
- '@csstools/css-syntax-patches-for-csstree': 1.1.6(css-tree@3.2.1)
+ '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1)
'@exodus/bytes': 1.15.0(@noble/hashes@2.2.0)
css-tree: 3.2.1
data-urls: 7.0.0(@noble/hashes@2.2.0)
@@ -6428,27 +6023,27 @@ snapshots:
keycharm@0.4.0: {}
- keyv@4.5.4:
+ keyv@4.0.0:
dependencies:
json-buffer: 3.0.1
- keyv@5.6.0:
+ keyv@4.5.4:
dependencies:
- '@keyv/serialize': 1.1.1
+ json-buffer: 3.0.1
- knip@6.24.0:
+ knip@6.29.0:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
formatly: 0.3.0
get-tsconfig: 4.14.0
jiti: 2.7.0
- oxc-parser: 0.137.0
- oxc-resolver: 11.21.3
+ oxc-parser: 0.140.0
+ oxc-resolver: 11.24.2
picomatch: 4.0.4
smol-toml: 1.7.0
strip-json-comments: 5.0.3
tinyglobby: 0.2.17
- unbash: 4.0.2
+ unbash: 4.0.3
yaml: 2.9.0
zod: 4.4.3
@@ -6472,36 +6067,69 @@ snapshots:
lightningcss-android-arm64@1.32.0:
optional: true
+ lightningcss-android-arm64@1.33.0:
+ optional: true
+
lightningcss-darwin-arm64@1.32.0:
optional: true
+ lightningcss-darwin-arm64@1.33.0:
+ optional: true
+
lightningcss-darwin-x64@1.32.0:
optional: true
+ lightningcss-darwin-x64@1.33.0:
+ optional: true
+
lightningcss-freebsd-x64@1.32.0:
optional: true
+ lightningcss-freebsd-x64@1.33.0:
+ optional: true
+
lightningcss-linux-arm-gnueabihf@1.32.0:
optional: true
+ lightningcss-linux-arm-gnueabihf@1.33.0:
+ optional: true
+
lightningcss-linux-arm64-gnu@1.32.0:
optional: true
+ lightningcss-linux-arm64-gnu@1.33.0:
+ optional: true
+
lightningcss-linux-arm64-musl@1.32.0:
optional: true
+ lightningcss-linux-arm64-musl@1.33.0:
+ optional: true
+
lightningcss-linux-x64-gnu@1.32.0:
optional: true
+ lightningcss-linux-x64-gnu@1.33.0:
+ optional: true
+
lightningcss-linux-x64-musl@1.32.0:
optional: true
+ lightningcss-linux-x64-musl@1.33.0:
+ optional: true
+
lightningcss-win32-arm64-msvc@1.32.0:
optional: true
+ lightningcss-win32-arm64-msvc@1.33.0:
+ optional: true
+
lightningcss-win32-x64-msvc@1.32.0:
optional: true
+ lightningcss-win32-x64-msvc@1.33.0:
+ optional: true
+
lightningcss@1.32.0:
dependencies:
detect-libc: 2.0.3
@@ -6518,6 +6146,22 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ lightningcss@1.33.0:
+ dependencies:
+ detect-libc: 2.0.3
+ optionalDependencies:
+ lightningcss-android-arm64: 1.33.0
+ lightningcss-darwin-arm64: 1.33.0
+ lightningcss-darwin-x64: 1.33.0
+ lightningcss-freebsd-x64: 1.33.0
+ lightningcss-linux-arm-gnueabihf: 1.33.0
+ lightningcss-linux-arm64-gnu: 1.33.0
+ lightningcss-linux-arm64-musl: 1.33.0
+ lightningcss-linux-x64-gnu: 1.33.0
+ lightningcss-linux-x64-musl: 1.33.0
+ lightningcss-win32-arm64-msvc: 1.33.0
+ lightningcss-win32-x64-msvc: 1.33.0
+
locate-path@3.0.0:
dependencies:
p-locate: 3.0.0
@@ -6537,14 +6181,14 @@ snapshots:
lodash@4.18.1: {}
- lowercase-keys@3.0.0: {}
-
- lowercase-keys@4.0.1: {}
+ lowercase-keys@2.0.0: {}
lru-cache@10.2.0: {}
lru-cache@11.1.0: {}
+ lru-cache@11.2.1: {}
+
lru-cache@11.3.5: {}
lru-cache@6.0.0:
@@ -6565,9 +6209,26 @@ snapshots:
dependencies:
semver: 7.5.2
+ make-fetch-happen@15.0.6:
+ dependencies:
+ '@gar/promise-retry': 1.0.3
+ '@npmcli/agent': 4.0.2
+ '@npmcli/redact': 4.0.0
+ cacache: 20.0.4
+ http-cache-semantics: 4.1.1
+ minipass: 7.0.2
+ minipass-fetch: 5.0.2
+ minipass-flush: 1.0.7
+ minipass-pipeline: 1.2.4
+ negotiator: 1.0.0
+ proc-log: 6.0.0
+ ssri: 13.0.1
+ transitivePeerDependencies:
+ - supports-color
+
mapbox-to-css-font@3.2.0: {}
- marked@18.0.5: {}
+ marked@18.0.7: {}
matcher@3.0.0:
dependencies:
@@ -6576,13 +6237,11 @@ snapshots:
math-intrinsics@1.0.0: {}
- math-intrinsics@1.1.0: {}
-
mdn-data@2.27.1: {}
mem@3.0.1:
dependencies:
- mimic-fn: 1.2.0
+ mimic-fn: 1.0.0
p-is-promise: 1.1.0
micron-parser@https://codeload.github.com/RFnexus/micron-parser-js/tar.gz/33feb1054c8b2cb3f5f05abbb8903360d3f0c098: {}
@@ -6593,15 +6252,21 @@ snapshots:
dependencies:
mime-db: 1.52.0
- mime@2.6.0: {}
+ mime@2.5.2: {}
- mimic-fn@1.2.0: {}
+ mimic-fn@1.0.0: {}
- mimic-response@4.0.0: {}
+ mimic-response@1.0.0: {}
+
+ mimic-response@3.1.0: {}
mini-svg-data-uri@1.2.3: {}
minimatch@10.2.3:
+ dependencies:
+ brace-expansion: 1.1.13
+
+ minimatch@10.2.5:
dependencies:
brace-expansion: 5.0.7
@@ -6611,27 +6276,68 @@ snapshots:
minimist@1.2.8: {}
- minipass@7.1.2: {}
+ minipass-collect@2.0.1:
+ dependencies:
+ minipass: 7.0.3
- minizlib@3.1.0:
+ minipass-fetch@5.0.2:
+ dependencies:
+ minipass: 7.0.3
+ minipass-sized: 2.0.0
+ minizlib: 3.0.1
+ optionalDependencies:
+ iconv-lite: 0.7.2
+
+ minipass-flush@1.0.7:
+ dependencies:
+ minipass: 3.0.0
+
+ minipass-pipeline@1.2.4:
+ dependencies:
+ minipass: 3.0.0
+
+ minipass-sized@2.0.0:
dependencies:
minipass: 7.1.2
- mkdirp@0.5.1:
+ minipass@3.0.0:
dependencies:
- minimist: 1.2.8
+ yallist: 4.0.0
+
+ minipass@7.0.2: {}
+
+ minipass@7.0.3: {}
+
+ minipass@7.0.4: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@3.0.1:
+ dependencies:
+ minipass: 7.0.4
+ rimraf: 5.0.5
+
+ minizlib@3.1.0:
+ dependencies:
+ minipass: 7.1.2
mrmime@2.0.0: {}
ms@2.0.0: {}
+ ms@2.1.3: {}
+
muggle-string@0.4.1: {}
nanoid@3.3.12: {}
+ nanoid@3.3.16: {}
+
natural-compare@1.4.0: {}
- node-abi@4.31.0:
+ negotiator@1.0.0: {}
+
+ node-abi@4.2.0:
dependencies:
semver: 7.5.2
@@ -6639,32 +6345,34 @@ snapshots:
dependencies:
semver: 7.5.2
- node-gyp@12.4.0:
+ node-gyp@12.2.0:
dependencies:
env-paths: 2.2.0
exponential-backoff: 3.1.1
graceful-fs: 4.2.6
+ make-fetch-happen: 15.0.6
nopt: 9.0.0
- proc-log: 6.1.0
+ proc-log: 6.0.0
semver: 7.5.2
- tar: 7.5.19
+ tar: 7.5.21
tinyglobby: 0.2.12
- undici: 7.28.0
which: 6.0.0
+ transitivePeerDependencies:
+ - supports-color
node-int64@0.4.0: {}
- nopt@7.2.1:
+ nopt@6.0.0:
dependencies:
- abbrev: 2.0.0
+ abbrev: 1.0.3
nopt@9.0.0:
dependencies:
abbrev: 4.0.0
- normalize-url@8.1.1: {}
+ normalize-url@6.0.1: {}
- npm-run-path@2.0.2:
+ npm-run-path@2.0.0:
dependencies:
path-key: 2.0.0
@@ -6680,48 +6388,42 @@ snapshots:
object-assign@4.1.0: {}
- object-inspect@1.13.4: {}
-
- object-keys@1.1.1: {}
+ object-keys@1.0.12:
+ optional: true
- object.assign@4.1.7:
- dependencies:
- call-bind: 1.0.9
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-object-atoms: 1.1.2
- has-symbols: 1.1.0
- object-keys: 1.1.1
+ object-keys@1.0.8: {}
obug@2.1.1: {}
ol-mapbox-style@13.4.1(patch_hash=a41b77469731f99ec75834cf2703f75fd9a5c573a7e70ec5c866849e58ac8b9d)(ol@10.9.0(patch_hash=c7d2f18804523893b6652fabb2e43ca5120d818dbfc92b710a3f5ab3745d4ef4)):
dependencies:
- '@maplibre/maplibre-gl-style-spec': 24.10.0
+ '@maplibre/maplibre-gl-style-spec': 24.4.1
mapbox-to-css-font: 3.2.0
ol: 10.9.0(patch_hash=c7d2f18804523893b6652fabb2e43ca5120d818dbfc92b710a3f5ab3745d4ef4)
ol@10.9.0(patch_hash=c7d2f18804523893b6652fabb2e43ca5120d818dbfc92b710a3f5ab3745d4ef4):
dependencies:
'@types/rbush': 4.0.0
- earcut: 3.1.0
+ earcut: 3.0.0
geotiff: 3.0.5
pbf: 4.0.1
rbush: 4.0.0
zarrita: 0.7.1
- once@1.4.0:
+ once@1.3.0: {}
+
+ once@1.3.1:
dependencies:
- wrappy: 1.0.2
+ wrappy: 1.0.0
- optionator@0.9.3:
+ optionator@0.9.4:
dependencies:
- '@aashutoshrathi/word-wrap': 1.2.6
- deep-is: 0.1.3
+ deep-is: 0.1.4
fast-levenshtein: 2.0.6
levn: 0.4.1
prelude-ls: 1.2.1
type-check: 0.4.0
+ word-wrap: 1.2.5
os-locale@3.0.0:
dependencies:
@@ -6729,58 +6431,54 @@ snapshots:
lcid: 2.0.0
mem: 3.0.1
- own-keys@1.0.1:
+ oxc-parser@0.140.0:
dependencies:
- get-intrinsic: 1.3.0
- object-keys: 1.1.1
- safe-push-apply: 1.0.0
-
- oxc-parser@0.137.0:
- dependencies:
- '@oxc-project/types': 0.137.0
+ '@oxc-project/types': 0.140.0
optionalDependencies:
- '@oxc-parser/binding-android-arm-eabi': 0.137.0
- '@oxc-parser/binding-android-arm64': 0.137.0
- '@oxc-parser/binding-darwin-arm64': 0.137.0
- '@oxc-parser/binding-darwin-x64': 0.137.0
- '@oxc-parser/binding-freebsd-x64': 0.137.0
- '@oxc-parser/binding-linux-arm-gnueabihf': 0.137.0
- '@oxc-parser/binding-linux-arm-musleabihf': 0.137.0
- '@oxc-parser/binding-linux-arm64-gnu': 0.137.0
- '@oxc-parser/binding-linux-arm64-musl': 0.137.0
- '@oxc-parser/binding-linux-ppc64-gnu': 0.137.0
- '@oxc-parser/binding-linux-riscv64-gnu': 0.137.0
- '@oxc-parser/binding-linux-riscv64-musl': 0.137.0
- '@oxc-parser/binding-linux-s390x-gnu': 0.137.0
- '@oxc-parser/binding-linux-x64-gnu': 0.137.0
- '@oxc-parser/binding-linux-x64-musl': 0.137.0
- '@oxc-parser/binding-openharmony-arm64': 0.137.0
- '@oxc-parser/binding-wasm32-wasi': 0.137.0
- '@oxc-parser/binding-win32-arm64-msvc': 0.137.0
- '@oxc-parser/binding-win32-ia32-msvc': 0.137.0
- '@oxc-parser/binding-win32-x64-msvc': 0.137.0
-
- oxc-resolver@11.21.3:
+ '@oxc-parser/binding-android-arm-eabi': 0.140.0
+ '@oxc-parser/binding-android-arm64': 0.140.0
+ '@oxc-parser/binding-darwin-arm64': 0.140.0
+ '@oxc-parser/binding-darwin-x64': 0.140.0
+ '@oxc-parser/binding-freebsd-x64': 0.140.0
+ '@oxc-parser/binding-linux-arm-gnueabihf': 0.140.0
+ '@oxc-parser/binding-linux-arm-musleabihf': 0.140.0
+ '@oxc-parser/binding-linux-arm64-gnu': 0.140.0
+ '@oxc-parser/binding-linux-arm64-musl': 0.140.0
+ '@oxc-parser/binding-linux-ppc64-gnu': 0.140.0
+ '@oxc-parser/binding-linux-riscv64-gnu': 0.140.0
+ '@oxc-parser/binding-linux-riscv64-musl': 0.140.0
+ '@oxc-parser/binding-linux-s390x-gnu': 0.140.0
+ '@oxc-parser/binding-linux-x64-gnu': 0.140.0
+ '@oxc-parser/binding-linux-x64-musl': 0.140.0
+ '@oxc-parser/binding-openharmony-arm64': 0.140.0
+ '@oxc-parser/binding-wasm32-wasi': 0.140.0
+ '@oxc-parser/binding-win32-arm64-msvc': 0.140.0
+ '@oxc-parser/binding-win32-ia32-msvc': 0.140.0
+ '@oxc-parser/binding-win32-x64-msvc': 0.140.0
+
+ oxc-resolver@11.24.2:
optionalDependencies:
- '@oxc-resolver/binding-android-arm-eabi': 11.21.3
- '@oxc-resolver/binding-android-arm64': 11.21.3
- '@oxc-resolver/binding-darwin-arm64': 11.21.3
- '@oxc-resolver/binding-darwin-x64': 11.21.3
- '@oxc-resolver/binding-freebsd-x64': 11.21.3
- '@oxc-resolver/binding-linux-arm-gnueabihf': 11.21.3
- '@oxc-resolver/binding-linux-arm-musleabihf': 11.21.3
- '@oxc-resolver/binding-linux-arm64-gnu': 11.21.3
- '@oxc-resolver/binding-linux-arm64-musl': 11.21.3
- '@oxc-resolver/binding-linux-ppc64-gnu': 11.21.3
- '@oxc-resolver/binding-linux-riscv64-gnu': 11.21.3
- '@oxc-resolver/binding-linux-riscv64-musl': 11.21.3
- '@oxc-resolver/binding-linux-s390x-gnu': 11.21.3
- '@oxc-resolver/binding-linux-x64-gnu': 11.21.3
- '@oxc-resolver/binding-linux-x64-musl': 11.21.3
- '@oxc-resolver/binding-openharmony-arm64': 11.21.3
- '@oxc-resolver/binding-wasm32-wasi': 11.21.3
- '@oxc-resolver/binding-win32-arm64-msvc': 11.21.3
- '@oxc-resolver/binding-win32-x64-msvc': 11.21.3
+ '@oxc-resolver/binding-android-arm-eabi': 11.24.2
+ '@oxc-resolver/binding-android-arm64': 11.24.2
+ '@oxc-resolver/binding-darwin-arm64': 11.24.2
+ '@oxc-resolver/binding-darwin-x64': 11.24.2
+ '@oxc-resolver/binding-freebsd-x64': 11.24.2
+ '@oxc-resolver/binding-linux-arm-gnueabihf': 11.24.2
+ '@oxc-resolver/binding-linux-arm-musleabihf': 11.24.2
+ '@oxc-resolver/binding-linux-arm64-gnu': 11.24.2
+ '@oxc-resolver/binding-linux-arm64-musl': 11.24.2
+ '@oxc-resolver/binding-linux-ppc64-gnu': 11.24.2
+ '@oxc-resolver/binding-linux-riscv64-gnu': 11.24.2
+ '@oxc-resolver/binding-linux-riscv64-musl': 11.24.2
+ '@oxc-resolver/binding-linux-s390x-gnu': 11.24.2
+ '@oxc-resolver/binding-linux-x64-gnu': 11.24.2
+ '@oxc-resolver/binding-linux-x64-musl': 11.24.2
+ '@oxc-resolver/binding-openharmony-arm64': 11.24.2
+ '@oxc-resolver/binding-wasm32-wasi': 11.24.2
+ '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2
+ '@oxc-resolver/binding-win32-x64-msvc': 11.24.2
+
+ p-cancelable@2.0.0: {}
p-finally@1.0.0: {}
@@ -6792,11 +6490,7 @@ snapshots:
p-limit@2.2.0:
dependencies:
- p-try: 2.2.0
-
- p-limit@3.0.2:
- dependencies:
- p-try: 2.2.0
+ p-try: 2.0.0
p-limit@3.1.0:
dependencies:
@@ -6812,11 +6506,11 @@ snapshots:
p-locate@5.0.0:
dependencies:
- p-limit: 3.0.2
+ p-limit: 3.1.0
- p-try@2.0.0: {}
+ p-map@7.0.6: {}
- p-try@2.2.0: {}
+ p-try@2.0.0: {}
package-json-from-dist@1.0.0: {}
@@ -6824,14 +6518,14 @@ snapshots:
pako@2.0.4: {}
- parent-module@1.0.0:
+ parent-module@1.0.1:
dependencies:
- callsites: 3.0.0
+ callsites: 3.1.0
parse-headers@2.0.2:
dependencies:
- for-each: 0.3.5
- string.prototype.trim: 1.2.11
+ for-each: 0.3.3
+ string.prototype.trim: 1.1.2
parse5@8.0.1:
dependencies:
@@ -6849,6 +6543,8 @@ snapshots:
path-key@3.1.0: {}
+ path-key@3.1.1: {}
+
path-scurry@1.11.1:
dependencies:
lru-cache: 10.2.0
@@ -6860,7 +6556,7 @@ snapshots:
dependencies:
resolve-protobuf-schema: 2.1.0
- pe-library@0.4.1: {}
+ pe-library@0.4.0: {}
picocolors@1.1.1: {}
@@ -6872,7 +6568,7 @@ snapshots:
asn1js: 3.0.10
bytestreamjs: 2.0.1
pvtsutils: 1.3.6
- pvutils: 1.1.5
+ pvutils: 1.1.3
tslib: 2.8.1
playwright-core@1.61.1: {}
@@ -6890,22 +6586,20 @@ snapshots:
pngjs@5.0.0: {}
- possible-typed-array-names@1.1.0: {}
-
- postcss-selector-parser@7.1.0:
+ postcss-selector-parser@7.1.4:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
- postcss@8.5.15:
+ postcss@8.5.19:
dependencies:
nanoid: 3.3.12
picocolors: 1.1.1
source-map-js: 1.2.1
- postcss@8.5.16:
+ postcss@8.5.22:
dependencies:
- nanoid: 3.3.12
+ nanoid: 3.3.16
picocolors: 1.1.1
source-map-js: 1.2.1
@@ -6918,13 +6612,13 @@ snapshots:
prettier-linter-helpers@1.0.1:
dependencies:
- fast-diff: 1.3.0
+ fast-diff: 1.1.2
- prettier@3.9.3: {}
+ prettier@3.9.6: {}
private@0.1.5: {}
- proc-log@6.1.0: {}
+ proc-log@6.0.0: {}
process-nextick-args@1.0.0: {}
@@ -6934,7 +6628,7 @@ snapshots:
promise-retry@2.0.1:
dependencies:
- err-code: 2.0.3
+ err-code: 2.0.2
retry: 0.12.0
proper-lockfile@4.1.2:
@@ -6943,11 +6637,14 @@ snapshots:
retry: 0.12.0
signal-exit: 3.0.2
- proto-list@1.2.4: {}
+ proto-list@1.2.1: {}
protocol-buffers-schema@3.6.1: {}
- punycode@2.1.0: {}
+ pump@3.0.0:
+ dependencies:
+ end-of-stream: 1.1.0
+ once: 1.3.1
punycode@2.3.1: {}
@@ -6955,6 +6652,8 @@ snapshots:
dependencies:
tslib: 2.8.1
+ pvutils@1.1.3: {}
+
pvutils@1.1.5: {}
qrcode@1.5.4:
@@ -7014,41 +6713,21 @@ snapshots:
string_decoder: 1.1.1
util-deprecate: 1.0.1
- recast@0.10.43:
+ recast@0.10.1:
dependencies:
- ast-types: 0.8.15
- esprima-fb: 15001.1001.0-dev-harmony-fb
+ ast-types: 0.6.12
+ esprima-fb: 12001.1.0-dev-harmony-fb
private: 0.1.5
- source-map: 0.5.0
+ source-map: 0.3.0
reference-spec-reader@0.2.0: {}
- reflect.getprototypeof@1.0.10:
- dependencies:
- call-bind: 1.0.9
- define-properties: 1.2.1
- es-abstract: 1.24.2
- es-errors: 1.3.0
- es-object-atoms: 1.1.2
- get-intrinsic: 1.3.0
- get-proto: 1.0.1
- which-builtin-type: 1.2.1
-
regexp-tree@0.1.1:
dependencies:
cli-table3: 0.5.0
colors: 1.1.2
yargs: 12.0.5
- regexp.prototype.flags@1.5.4:
- dependencies:
- call-bind: 1.0.9
- define-properties: 1.2.1
- es-errors: 1.3.0
- get-proto: 1.0.1
- gopd: 1.2.0
- set-function-name: 2.0.2
-
require-directory@2.1.1: {}
require-from-string@2.0.2: {}
@@ -7057,11 +6736,11 @@ snapshots:
require-main-filename@2.0.0: {}
- resedit@1.7.2:
+ resedit@1.7.0:
dependencies:
- pe-library: 0.4.1
+ pe-library: 0.4.0
- resolve-alpn@1.2.1: {}
+ resolve-alpn@1.0.0: {}
resolve-from@4.0.0: {}
@@ -7071,9 +6750,9 @@ snapshots:
dependencies:
protocol-buffers-schema: 3.6.1
- responselike@4.0.2:
+ responselike@2.0.0:
dependencies:
- lowercase-keys: 3.0.0
+ lowercase-keys: 2.0.0
retry@0.12.0: {}
@@ -7085,65 +6764,51 @@ snapshots:
dependencies:
glob: 10.5.0
- roarr@2.15.4:
+ roarr@2.15.3:
dependencies:
- boolean: 3.2.0
- detect-node: 2.1.0
- globalthis: 1.0.4
+ boolean: 3.0.1
+ detect-node: 2.0.4
+ globalthis: 1.0.1
json-stringify-safe: 5.0.1
semver-compare: 1.0.0
- sprintf-js: 1.1.3
+ sprintf-js: 1.1.2
optional: true
- rolldown@1.0.3:
+ rolldown@1.1.5:
dependencies:
- '@oxc-project/types': 0.133.0
+ '@oxc-project/types': 0.139.0
'@rolldown/pluginutils': 1.0.0
optionalDependencies:
- '@rolldown/binding-android-arm64': 1.0.3
- '@rolldown/binding-darwin-arm64': 1.0.3
- '@rolldown/binding-darwin-x64': 1.0.3
- '@rolldown/binding-freebsd-x64': 1.0.3
- '@rolldown/binding-linux-arm-gnueabihf': 1.0.3
- '@rolldown/binding-linux-arm64-gnu': 1.0.3
- '@rolldown/binding-linux-arm64-musl': 1.0.3
- '@rolldown/binding-linux-ppc64-gnu': 1.0.3
- '@rolldown/binding-linux-s390x-gnu': 1.0.3
- '@rolldown/binding-linux-x64-gnu': 1.0.3
- '@rolldown/binding-linux-x64-musl': 1.0.3
- '@rolldown/binding-openharmony-arm64': 1.0.3
- '@rolldown/binding-wasm32-wasi': 1.0.3
- '@rolldown/binding-win32-arm64-msvc': 1.0.3
- '@rolldown/binding-win32-x64-msvc': 1.0.3
-
- safe-array-concat@1.1.4:
- dependencies:
- call-bind: 1.0.9
- call-bound: 1.0.4
- get-intrinsic: 1.3.0
- has-symbols: 1.1.0
- isarray: 2.0.5
+ '@rolldown/binding-android-arm64': 1.1.5
+ '@rolldown/binding-darwin-arm64': 1.1.5
+ '@rolldown/binding-darwin-x64': 1.1.5
+ '@rolldown/binding-freebsd-x64': 1.1.5
+ '@rolldown/binding-linux-arm-gnueabihf': 1.1.5
+ '@rolldown/binding-linux-arm64-gnu': 1.1.5
+ '@rolldown/binding-linux-arm64-musl': 1.1.5
+ '@rolldown/binding-linux-ppc64-gnu': 1.1.5
+ '@rolldown/binding-linux-s390x-gnu': 1.1.5
+ '@rolldown/binding-linux-x64-gnu': 1.1.5
+ '@rolldown/binding-linux-x64-musl': 1.1.5
+ '@rolldown/binding-openharmony-arm64': 1.1.5
+ '@rolldown/binding-wasm32-wasi': 1.1.5
+ '@rolldown/binding-win32-arm64-msvc': 1.1.5
+ '@rolldown/binding-win32-x64-msvc': 1.1.5
+
+ rw@1.3.3: {}
safe-buffer@5.1.1: {}
- safe-push-apply@1.0.0:
- dependencies:
- es-errors: 1.3.0
- isarray: 2.0.5
-
- safe-regex-test@1.1.0:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-regex: 1.2.1
-
safe-regex@2.1.1:
dependencies:
regexp-tree: 0.1.1
- sanitize-filename@1.6.4:
+ safer-buffer@2.1.2:
+ optional: true
+
+ sanitize-filename@1.6.3:
dependencies:
- truncate-utf8-bytes: 1.0.2
+ truncate-utf8-bytes: 1.0.0
sax@1.2.4: {}
@@ -7165,28 +6830,6 @@ snapshots:
set-blocking@2.0.0: {}
- set-function-length@1.2.2:
- dependencies:
- define-data-property: 1.1.4
- es-errors: 1.3.0
- function-bind: 1.1.2
- get-intrinsic: 1.3.0
- gopd: 1.0.1
- has-property-descriptors: 1.0.2
-
- set-function-name@2.0.2:
- dependencies:
- define-data-property: 1.1.4
- es-errors: 1.3.0
- functions-have-names: 1.2.3
- has-property-descriptors: 1.0.2
-
- set-proto@1.0.0:
- dependencies:
- dunder-proto: 1.0.1
- es-errors: 1.3.0
- es-object-atoms: 1.1.2
-
setimmediate@1.0.5: {}
shebang-command@2.0.0:
@@ -7195,34 +6838,6 @@ snapshots:
shebang-regex@3.0.0: {}
- side-channel-list@1.0.1:
- dependencies:
- es-errors: 1.3.0
- object-inspect: 1.13.4
-
- side-channel-map@1.0.1:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- get-intrinsic: 1.2.6
- object-inspect: 1.13.4
-
- side-channel-weakmap@1.0.2:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- get-intrinsic: 1.2.6
- object-inspect: 1.13.4
- side-channel-map: 1.0.1
-
- side-channel@1.1.1:
- dependencies:
- es-errors: 1.3.0
- object-inspect: 1.13.4
- side-channel-list: 1.0.1
- side-channel-map: 1.0.1
- side-channel-weakmap: 1.0.2
-
siginfo@2.0.0: {}
signal-exit@3.0.0: {}
@@ -7241,8 +6856,23 @@ snapshots:
mrmime: 2.0.0
totalist: 3.0.0
+ smart-buffer@4.2.0: {}
+
smol-toml@1.7.0: {}
+ socks-proxy-agent@8.0.3:
+ dependencies:
+ agent-base: 7.1.1
+ debug: 4.3.1
+ socks: 2.8.0
+ transitivePeerDependencies:
+ - supports-color
+
+ socks@2.8.0:
+ dependencies:
+ ip-address: 9.0.5
+ smart-buffer: 4.2.0
+
source-map-js@1.0.2: {}
source-map-js@1.2.1: {}
@@ -7255,26 +6885,31 @@ snapshots:
source-map-support@0.5.20:
dependencies:
buffer-from: 1.0.0
- source-map: 0.6.0
+ source-map: 0.6.1
- source-map@0.5.0: {}
+ source-map@0.3.0:
+ dependencies:
+ amdefine: 1.0.1
source-map@0.6.0: {}
- sprintf-js@1.1.3:
+ source-map@0.6.1: {}
+
+ sprintf-js@1.1.2:
optional: true
+ sprintf-js@1.1.3: {}
+
+ ssri@13.0.1:
+ dependencies:
+ minipass: 7.0.3
+
stackback@0.0.2: {}
stat-mode@1.0.0: {}
std-env@4.0.0-rc.1: {}
- stop-iteration-iterator@1.1.0:
- dependencies:
- es-errors: 1.3.0
- internal-slot: 1.1.0
-
string-width@1.0.1:
dependencies:
code-point-at: 1.0.0
@@ -7309,29 +6944,11 @@ snapshots:
emoji-regex: 9.2.2
strip-ansi: 6.0.1
- string.prototype.trim@1.2.11:
+ string.prototype.trim@1.1.2:
dependencies:
- call-bind: 1.0.9
- call-bound: 1.0.4
- define-data-property: 1.1.4
- define-properties: 1.2.1
- es-abstract: 1.24.2
- es-object-atoms: 1.1.2
- has-property-descriptors: 1.0.2
- safe-regex-test: 1.1.0
-
- string.prototype.trimend@1.0.10:
- dependencies:
- call-bind: 1.0.9
- call-bound: 1.0.4
- define-properties: 1.2.1
- es-object-atoms: 1.1.2
-
- string.prototype.trimstart@1.0.8:
- dependencies:
- call-bind: 1.0.9
- define-properties: 1.2.1
- es-object-atoms: 1.1.2
+ define-properties: 1.1.2
+ es-abstract: 1.5.0
+ function-bind: 1.0.2
string_decoder@0.10.24: {}
@@ -7359,21 +6976,21 @@ snapshots:
dependencies:
has-flag: 4.0.0
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
symbol-tree@3.2.4: {}
synckit@0.11.13:
dependencies:
'@pkgr/core': 0.3.6
- tagged-tag@1.0.0: {}
+ tailwindcss@4.3.3: {}
- tailwindcss@4.2.4: {}
+ tapable@2.3.3: {}
- tailwindcss@4.3.0: {}
-
- tapable@2.3.0: {}
-
- tar@7.5.19:
+ tar@7.5.21:
dependencies:
'@isaacs/fs-minipass': 4.0.0
chownr: 3.0.0
@@ -7386,12 +7003,11 @@ snapshots:
async-exit-hook: 2.0.1
fs-extra: 10.1.0
- temp@0.9.4:
+ temp@0.9.0:
dependencies:
- mkdirp: 0.5.1
rimraf: 2.6.2
- terser@5.48.0:
+ terser@5.49.0:
dependencies:
'@jridgewell/source-map': 0.3.3
acorn: 8.15.0
@@ -7406,7 +7022,7 @@ snapshots:
through2@0.6.5:
dependencies:
readable-stream: 1.0.33-1
- xtend: 4.0.2
+ xtend: 4.0.0
tiny-async-pool@1.3.0:
dependencies:
@@ -7435,13 +7051,13 @@ snapshots:
tinyrainbow@3.1.0: {}
- tldts-core@7.4.5: {}
+ tldts-core@7.0.5: {}
- tldts@7.4.5:
+ tldts@7.0.5:
dependencies:
- tldts-core: 7.4.5
+ tldts-core: 7.0.5
- tmp-promise@3.0.3:
+ tmp-promise@3.0.2:
dependencies:
tmp: 0.2.7
@@ -7451,17 +7067,18 @@ snapshots:
tough-cookie@6.0.1:
dependencies:
- tldts: 7.4.5
+ tldts: 7.0.5
tr46@6.0.0:
dependencies:
punycode: 2.3.1
- truncate-utf8-bytes@1.0.2:
+ truncate-utf8-bytes@1.0.0:
dependencies:
- utf8-byte-length: 1.0.5
+ utf8-byte-length: 1.0.1
- tslib@2.4.0: {}
+ tslib@2.4.0:
+ optional: true
tslib@2.8.1: {}
@@ -7472,55 +7089,9 @@ snapshots:
type-fest@0.13.1:
optional: true
- type-fest@5.7.0:
- dependencies:
- tagged-tag: 1.0.0
-
- typed-array-buffer@1.0.3:
- dependencies:
- call-bound: 1.0.4
- es-errors: 1.3.0
- is-typed-array: 1.1.15
-
- typed-array-byte-length@1.0.3:
- dependencies:
- call-bind: 1.0.9
- for-each: 0.3.5
- gopd: 1.2.0
- has-proto: 1.2.0
- is-typed-array: 1.1.15
-
- typed-array-byte-offset@1.0.4:
- dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.9
- for-each: 0.3.5
- gopd: 1.2.0
- has-proto: 1.2.0
- is-typed-array: 1.1.15
- reflect.getprototypeof: 1.0.10
-
- typed-array-length@1.0.8:
- dependencies:
- call-bind: 1.0.9
- for-each: 0.3.5
- gopd: 1.2.0
- is-typed-array: 1.1.15
- possible-typed-array-names: 1.1.0
- reflect.getprototypeof: 1.0.10
-
typescript@6.0.3: {}
- uint8array-extras@1.5.0: {}
-
- unbash@4.0.2: {}
-
- unbox-primitive@1.1.0:
- dependencies:
- call-bound: 1.0.4
- has-bigints: 1.1.0
- has-symbols: 1.1.0
- which-boxed-primitive: 1.1.1
+ unbash@4.0.3: {}
undici-types@7.16.0: {}
@@ -7535,26 +7106,26 @@ snapshots:
unreachable-branch-transform@0.3.0:
dependencies:
esmangle-evaluator: 1.0.0
- recast: 0.10.43
+ recast: 0.10.1
through2: 0.6.2
unzipit@2.0.0: {}
- unzipper@0.12.5:
+ unzipper@0.12.3:
dependencies:
bluebird: 3.7.2
duplexer2: 0.1.4
- fs-extra: 11.3.1
+ fs-extra: 11.2.0
graceful-fs: 4.2.2
node-int64: 0.4.0
upath@2.0.1: {}
- uri-js@4.2.2:
+ uri-js@4.4.1:
dependencies:
- punycode: 2.1.0
+ punycode: 2.3.1
- utf8-byte-length@1.0.5: {}
+ utf8-byte-length@1.0.1: {}
util-deprecate@1.0.1: {}
@@ -7581,41 +7152,41 @@ snapshots:
'@egjs/hammerjs': 2.0.17
component-emitter: 2.0.0
- vite-plugin-vuetify@2.1.3(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(vuetify@3.12.8):
+ vite-plugin-vuetify@2.1.3(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3))(vuetify@3.12.10):
dependencies:
- '@vuetify/loader-shared': 2.1.2(vue@3.5.39(typescript@6.0.3))(vuetify@3.12.8)
+ '@vuetify/loader-shared': 2.1.2(vue@3.5.40(typescript@6.0.3))(vuetify@3.12.10)
debug: 4.3.1
upath: 2.0.1
- vite: 8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)
- vue: 3.5.39(typescript@6.0.3)
- vuetify: 3.12.8(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.39(typescript@6.0.3))
+ vite: 8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)
+ vue: 3.5.40(typescript@6.0.3)
+ vuetify: 3.12.10(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.40(typescript@6.0.3))
transitivePeerDependencies:
- supports-color
- vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0):
+ vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0):
dependencies:
- lightningcss: 1.32.0
+ lightningcss: 1.33.0
picomatch: 4.0.4
- postcss: 8.5.16
- rolldown: 1.0.3
+ postcss: 8.5.22
+ rolldown: 1.1.5
tinyglobby: 0.2.17
optionalDependencies:
'@types/node': 24.9.0
fsevents: 2.3.3
jiti: 2.7.0
- terser: 5.48.0
+ terser: 5.49.0
yaml: 2.9.0
- vitest@4.1.5(@types/node@24.9.0)(@vitest/coverage-v8@4.1.5)(@vitest/ui@4.1.9)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)):
+ vitest@4.1.10(@types/node@24.9.0)(@vitest/coverage-v8@4.1.10)(@vitest/ui@4.1.10)(jsdom@29.1.1(@noble/hashes@2.2.0))(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies:
- '@vitest/expect': 4.1.5
- '@vitest/mocker': 4.1.5(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))
- '@vitest/pretty-format': 4.1.5
- '@vitest/runner': 4.1.5
- '@vitest/snapshot': 4.1.5
- '@vitest/spy': 4.1.5
- '@vitest/utils': 4.1.5
- es-module-lexer: 2.2.0
+ '@vitest/expect': 4.1.10
+ '@vitest/mocker': 4.1.10(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))
+ '@vitest/pretty-format': 4.1.10
+ '@vitest/runner': 4.1.10
+ '@vitest/snapshot': 4.1.10
+ '@vitest/spy': 4.1.10
+ '@vitest/utils': 4.1.10
+ es-module-lexer: 2.0.0
expect-type: 1.3.0
magic-string: 0.30.21
obug: 2.1.1
@@ -7626,24 +7197,24 @@ snapshots:
tinyexec: 1.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.1.0
- vite: 8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)
+ vite: 8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.9.0
- '@vitest/coverage-v8': 4.1.5(vitest@4.1.5)
- '@vitest/ui': 4.1.9(vitest@4.1.5)
+ '@vitest/coverage-v8': 4.1.10(vitest@4.1.10)
+ '@vitest/ui': 4.1.10(vitest@4.1.10)
jsdom: 29.1.1(@noble/hashes@2.2.0)
transitivePeerDependencies:
- msw
- vscode-uri@3.1.0: {}
+ vscode-uri@3.0.8: {}
- vue-component-type-helpers@3.3.5: {}
+ vue-component-type-helpers@3.0.0: {}
- vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0)):
+ vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)):
dependencies:
debug: 4.3.1
- eslint: 9.39.4(jiti@2.7.0)
+ eslint: 9.39.5(jiti@2.7.0)
eslint-scope: 8.2.0
eslint-visitor-keys: 4.2.0
espree: 10.3.0
@@ -7652,41 +7223,41 @@ snapshots:
transitivePeerDependencies:
- supports-color
- vue-i18n@11.4.6(vue@3.5.39(typescript@6.0.3)):
+ vue-i18n@11.4.7(vue@3.5.40(typescript@6.0.3)):
dependencies:
- '@intlify/core-base': 11.4.6
- '@intlify/devtools-types': 11.4.6
- '@intlify/shared': 11.4.6
- '@vue/devtools-api': 6.6.4
- vue: 3.5.39(typescript@6.0.3)
+ '@intlify/core-base': 11.4.7
+ '@intlify/devtools-types': 11.4.7
+ '@intlify/shared': 11.4.7
+ '@vue/devtools-api': 6.5.0
+ vue: 3.5.40(typescript@6.0.3)
- vue-router@4.6.4(vue@3.5.39(typescript@6.0.3)):
+ vue-router@4.6.4(vue@3.5.40(typescript@6.0.3)):
dependencies:
'@vue/devtools-api': 6.6.4
- vue: 3.5.39(typescript@6.0.3)
+ vue: 3.5.40(typescript@6.0.3)
- vue-tsc@3.3.6(typescript@6.0.3):
+ vue-tsc@3.3.7(typescript@6.0.3):
dependencies:
'@volar/typescript': 2.4.28
- '@vue/language-core': 3.3.6
+ '@vue/language-core': 3.3.7
typescript: 6.0.3
- vue@3.5.39(typescript@6.0.3):
+ vue@3.5.40(typescript@6.0.3):
dependencies:
- '@vue/compiler-dom': 3.5.39
- '@vue/compiler-sfc': 3.5.39
- '@vue/runtime-dom': 3.5.39
- '@vue/server-renderer': 3.5.39(vue@3.5.39(typescript@6.0.3))
- '@vue/shared': 3.5.39
+ '@vue/compiler-dom': 3.5.40
+ '@vue/compiler-sfc': 3.5.40
+ '@vue/runtime-dom': 3.5.40
+ '@vue/server-renderer': 3.5.40
+ '@vue/shared': 3.5.40
optionalDependencies:
typescript: 6.0.3
- vuetify@3.12.8(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.39(typescript@6.0.3)):
+ vuetify@3.12.10(typescript@6.0.3)(vite-plugin-vuetify@2.1.3)(vue@3.5.40(typescript@6.0.3)):
dependencies:
- vue: 3.5.39(typescript@6.0.3)
+ vue: 3.5.40(typescript@6.0.3)
optionalDependencies:
typescript: 6.0.3
- vite-plugin-vuetify: 2.1.3(vite@8.0.16(@types/node@24.9.0)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(vuetify@3.12.8)
+ vite-plugin-vuetify: 2.1.3(vite@8.1.5(@types/node@24.9.0)(jiti@2.7.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.40(typescript@6.0.3))(vuetify@3.12.10)
w3c-xmlserializer@5.0.0:
dependencies:
@@ -7698,7 +7269,7 @@ snapshots:
webcrypto-core@1.9.2:
dependencies:
- '@peculiar/asn1-schema': 2.8.0
+ '@peculiar/asn1-schema': 2.7.0
'@peculiar/json-schema': 1.1.12
'@peculiar/utils': 2.0.3
asn1js: 3.0.10
@@ -7716,64 +7287,19 @@ snapshots:
transitivePeerDependencies:
- '@noble/hashes'
- which-boxed-primitive@1.1.0:
- dependencies:
- is-bigint: 1.1.0
- is-boolean-object: 1.2.0
- is-number-object: 1.1.0
- is-string: 1.1.1
- is-symbol: 1.1.0
-
- which-boxed-primitive@1.1.1:
- dependencies:
- is-bigint: 1.1.0
- is-boolean-object: 1.2.2
- is-number-object: 1.1.1
- is-string: 1.1.1
- is-symbol: 1.1.1
-
- which-builtin-type@1.2.1:
- dependencies:
- call-bound: 1.0.4
- function.prototype.name: 1.2.0
- has-tostringtag: 1.0.2
- is-async-function: 2.1.1
- is-date-object: 1.1.0
- is-finalizationregistry: 1.1.1
- is-generator-function: 1.1.2
- is-regex: 1.2.1
- is-weakref: 1.1.1
- isarray: 2.0.5
- which-boxed-primitive: 1.1.0
- which-collection: 1.0.2
- which-typed-array: 1.1.22
-
- which-collection@1.0.2:
- dependencies:
- is-map: 2.0.3
- is-set: 2.0.3
- is-weakmap: 2.0.2
- is-weakset: 2.0.4
-
which-module@2.0.0: {}
- which-typed-array@1.1.22:
+ which@2.0.1:
dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.9
- call-bound: 1.0.4
- for-each: 0.3.5
- get-proto: 1.0.1
- gopd: 1.2.0
- has-tostringtag: 1.0.2
+ isexe: 2.0.0
- which@2.0.1:
+ which@2.0.2:
dependencies:
isexe: 2.0.0
which@5.0.0:
dependencies:
- isexe: 3.1.1
+ isexe: 3.1.5
which@6.0.0:
dependencies:
@@ -7784,6 +7310,8 @@ snapshots:
siginfo: 2.0.0
stackback: 0.0.2
+ word-wrap@1.2.5: {}
+
wrap-ansi@2.0.0:
dependencies:
string-width: 1.0.1
@@ -7806,9 +7334,7 @@ snapshots:
string-width: 5.1.2
strip-ansi: 6.0.1
- wrappy@1.0.2: {}
-
- xml-name-validator@4.0.0: {}
+ wrappy@1.0.0: {}
xml-name-validator@5.0.0: {}
@@ -7820,12 +7346,8 @@ snapshots:
xtend@4.0.0: {}
- xtend@4.0.2: {}
-
y18n@5.0.5: {}
- y18n@5.0.8: {}
-
yallist@4.0.0: {}
yallist@5.0.0: {}
@@ -7837,8 +7359,6 @@ snapshots:
camelcase: 5.0.0
decamelize: 1.2.0
- yargs-parser@22.0.0: {}
-
yargs@12.0.5:
dependencies:
cliui: 4.0.0
@@ -7868,15 +7388,15 @@ snapshots:
y18n: 5.0.5
yargs-parser: 18.1.1
- yargs@17.7.3:
+ yargs@17.6.2:
dependencies:
cliui: 8.0.1
escalade: 3.1.1
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
- y18n: 5.0.8
- yargs-parser: 22.0.0
+ y18n: 5.0.5
+ yargs-parser: 18.1.1
yocto-queue@0.1.0: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 05ca47dd..3a04e47c 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -54,7 +54,7 @@ overrides:
flatted: ">=3.4.2"
tar: ">=7.5.16"
tmp: ">=0.2.7"
- undici: ">=7.28.0"
+ undici: "7.28.0"
webpack: ">=5.104.0"
y18n: ">=5.0.5"
yargs-parser: ">=18.1.1"
diff --git a/tests/backend/eect/packs/test_auth_surface_pack.py b/tests/backend/eect/packs/test_auth_surface_pack.py
index 8a471602..26514218 100644
--- a/tests/backend/eect/packs/test_auth_surface_pack.py
+++ b/tests/backend/eect/packs/test_auth_surface_pack.py
@@ -33,6 +33,9 @@ _MUTATING_SAMPLES = (
("POST", "/api/v1/filesync/download", {"peer_id": "bb" * 16, "path": "a.txt"}),
("POST", "/api/v1/filesync/acl", {"enforce": False}),
("PATCH", "/api/v1/filesync/settings", {"monitor": True}),
+ ("POST", "/api/v1/filesync/mkdir", {"path": "folder"}),
+ ("DELETE", "/api/v1/filesync/entry", {"path": "a.txt"}),
+ ("POST", "/api/v1/lxmf/propagation-node/cancel-inbound", {}),
)
@@ -71,6 +74,19 @@ def _stub_filesync_handler(mock_app):
"announce_interval": 300,
"running": False,
}
+ handler.manager_mkdir.return_value = {"ok": True, "path": "folder"}
+ handler.manager_delete.return_value = {"ok": True, "path": "a.txt"}
+ handler.manager_upload.return_value = {"ok": True, "path": "a.txt", "size": 1}
+ handler.list_tree.return_value = {
+ "ok": True,
+ "current": "",
+ "parent": None,
+ "entries": [],
+ }
+ handler.manager_content.return_value = {
+ "ok": False,
+ "error": "path not allowed",
+ }
@pytest.mark.asyncio
diff --git a/tests/backend/fixtures/http_api_routes.json b/tests/backend/fixtures/http_api_routes.json
index 83ef20e7..c5e309ab 100644
--- a/tests/backend/fixtures/http_api_routes.json
+++ b/tests/backend/fixtures/http_api_routes.json
@@ -28,6 +28,10 @@
"method": "GET",
"path": "/api/v1/app/info"
},
+ {
+ "method": "GET",
+ "path": "/api/v1/app/sessions"
+ },
{
"method": "POST",
"path": "/api/v1/app/integrity/acknowledge"
@@ -320,6 +324,10 @@
"method": "POST",
"path": "/api/v1/filesync/connect"
},
+ {
+ "method": "GET",
+ "path": "/api/v1/filesync/content"
+ },
{
"method": "GET",
"path": "/api/v1/filesync/directories"
@@ -336,10 +344,18 @@
"method": "POST",
"path": "/api/v1/filesync/download"
},
+ {
+ "method": "DELETE",
+ "path": "/api/v1/filesync/entry"
+ },
{
"method": "GET",
"path": "/api/v1/filesync/files"
},
+ {
+ "method": "POST",
+ "path": "/api/v1/filesync/mkdir"
+ },
{
"method": "GET",
"path": "/api/v1/filesync/peers"
@@ -360,6 +376,14 @@
"method": "POST",
"path": "/api/v1/filesync/stop"
},
+ {
+ "method": "GET",
+ "path": "/api/v1/filesync/tree"
+ },
+ {
+ "method": "POST",
+ "path": "/api/v1/filesync/upload"
+ },
{
"method": "GET",
"path": "/api/v1/gifs"
diff --git a/tests/backend/http_api_response_registry.py b/tests/backend/http_api_response_registry.py
index 3c5f1206..ea506c0b 100644
--- a/tests/backend/http_api_response_registry.py
+++ b/tests/backend/http_api_response_registry.py
@@ -89,6 +89,7 @@ from tests.backend.http_api_response_schemas import (
FILESYNC_FILES_SCHEMA,
FILESYNC_PEERS_SCHEMA,
FILESYNC_STATUS_SCHEMA,
+ FILESYNC_TREE_SCHEMA,
RNPATH_RATES_SCHEMA,
RNPATH_TABLE_SCHEMA,
RNPATH_TRACE_SCHEMA,
@@ -491,6 +492,13 @@ HTTP_JSON_GET_CONTRACTS: tuple[HttpJsonContract, ...] = (
allow_statuses=(200, 400, 503),
alt_schemas=(MESSAGE_ENVELOPE_SCHEMA,),
),
+ HttpJsonContract(
+ "GET",
+ "/api/v1/filesync/tree",
+ FILESYNC_TREE_SCHEMA,
+ allow_statuses=(200, 400, 503),
+ alt_schemas=(MESSAGE_ENVELOPE_SCHEMA,),
+ ),
HttpJsonContract(
"GET",
"/api/v1/filesync/acl",
@@ -613,6 +621,7 @@ HTTP_JSON_GET_CONTRACT_EXCLUDED: tuple[str, ...] = (
"/api/v1/tools/rnode/download_firmware",
"/api/v1/tools/rnode/latest_release",
"/api/v1/tools/micron-parser-go-release",
+ "/api/v1/filesync/content",
"/api/v1/favourites/layout",
"/api/v1/map/overlays",
"/api/v1/map/overlays/jobs/{job_id}",
diff --git a/tests/backend/http_api_response_schemas.py b/tests/backend/http_api_response_schemas.py
index 5433446f..4c2f3ac2 100644
--- a/tests/backend/http_api_response_schemas.py
+++ b/tests/backend/http_api_response_schemas.py
@@ -581,6 +581,19 @@ FILESYNC_DIRECTORIES_SCHEMA: dict = {
"additionalProperties": True,
}
+FILESYNC_TREE_SCHEMA: dict = {
+ "type": "object",
+ "required": ["ok", "root", "current", "entries"],
+ "properties": {
+ "ok": _BOOLEAN,
+ "root": _STRING,
+ "current": _STRING,
+ "parent": {"type": ["string", "null"]},
+ "entries": _ARRAY,
+ },
+ "additionalProperties": True,
+}
+
FILESYNC_ACL_SCHEMA: dict = {
"type": "object",
"required": ["enforce", "rules"],
diff --git a/tests/backend/test_active_sessions.py b/tests/backend/test_active_sessions.py
new file mode 100644
index 00000000..021a4626
--- /dev/null
+++ b/tests/backend/test_active_sessions.py
@@ -0,0 +1,304 @@
+# SPDX-License-Identifier: 0BSD
+
+"""Active UI WebSocket session tracking and multi-session warning oracles."""
+
+from __future__ import annotations
+
+import json
+import shutil
+import tempfile
+from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
+import RNS
+from hypothesis import given, settings
+from hypothesis import strategies as st
+
+from meshchatx.src.backend.active_sessions import (
+ ActiveSessionTracker,
+ should_warn_multi_session,
+)
+
+
+@pytest.fixture
+def temp_dir():
+ dir_path = tempfile.mkdtemp()
+ yield dir_path
+ shutil.rmtree(dir_path)
+
+
+@pytest.fixture
+def mock_rns_minimal():
+ with (
+ patch("RNS.Reticulum") as mock_rns,
+ patch("RNS.Transport"),
+ patch("LXMF.LXMRouter"),
+ patch("meshchatx.meshchat.get_file_path", return_value="/tmp/mock_path"),
+ ):
+ mock_rns_instance = mock_rns.return_value
+ mock_rns_instance.configpath = "/tmp/mock_config"
+ mock_rns_instance.is_connected_to_shared_instance = False
+ mock_rns_instance.transport_enabled.return_value = True
+
+ mock_id = MagicMock(spec=RNS.Identity)
+ mock_id.hash = b"test_hash_32_bytes_long_01234567"
+ mock_id.hexhash = mock_id.hash.hex()
+ mock_id.get_private_key.return_value = b"test_private_key"
+ yield mock_id
+
+
+def test_should_warn_multi_session_oracle_edge_cases():
+ assert should_warn_multi_session(0, True) is False
+ assert should_warn_multi_session(1, True) is False
+ assert should_warn_multi_session(2, True) is True
+ assert should_warn_multi_session(3, True) is True
+ assert should_warn_multi_session(2, False) is False
+ assert should_warn_multi_session(99, False) is False
+ assert should_warn_multi_session("2", True) is True
+ assert should_warn_multi_session("nope", True) is False
+ assert should_warn_multi_session(None, True) is False
+
+
+@given(
+ count=st.integers(min_value=-5, max_value=50),
+ enabled=st.booleans(),
+)
+@settings(max_examples=80, deadline=None)
+def test_should_warn_multi_session_matches_count_threshold(count, enabled):
+ expected = bool(enabled) and int(count) >= 2
+ assert should_warn_multi_session(count, enabled) is expected
+
+
+def test_tracker_add_list_remove_round_trip():
+ tracker = ActiveSessionTracker()
+ assert tracker.count() == 0
+ assert tracker.list_sessions() == []
+
+ first = tracker.add(ip="127.0.0.1", user_agent="Browser/A")
+ second = tracker.add(ip="10.0.0.2", user_agent="Browser/B")
+ assert first["id"] != second["id"]
+ assert tracker.count() == 2
+
+ rows = tracker.list_sessions()
+ assert len(rows) == 2
+ assert rows[0]["ip"] == "127.0.0.1"
+ assert rows[0]["user_agent"] == "Browser/A"
+ assert rows[1]["ip"] == "10.0.0.2"
+ assert isinstance(rows[0]["connected_at"], float)
+
+ assert tracker.remove(first["id"]) is True
+ assert tracker.count() == 1
+ assert tracker.remove(first["id"]) is False
+ assert tracker.remove("") is False
+ assert tracker.remove(None) is False
+
+ snap = tracker.snapshot()
+ assert snap["count"] == 1
+ assert snap["sessions"][0]["id"] == second["id"]
+
+
+def test_tracker_sanitizes_ip_and_user_agent():
+ tracker = ActiveSessionTracker()
+ entry = tracker.add(ip=None, user_agent="\x00\x01bad\x7f agent")
+ assert entry["ip"] == "unknown"
+ assert "\x00" not in entry["user_agent"]
+ assert "bad" in entry["user_agent"]
+
+ long_ua = "x" * 2000
+ long_ip = "y" * 200
+ entry2 = tracker.add(ip=long_ip, user_agent=long_ua)
+ assert len(entry2["ip"]) <= 128
+ assert len(entry2["user_agent"]) <= 512
+
+
+def test_tracker_empty_user_agent_becomes_unknown():
+ tracker = ActiveSessionTracker()
+ entry = tracker.add(ip="192.168.1.1", user_agent=" ")
+ assert entry["user_agent"] == "unknown"
+
+
+@pytest.mark.asyncio
+async def test_app_sessions_endpoint_smoke(mock_rns_minimal, temp_dir):
+ from meshchatx.meshchat import ReticulumMeshChat
+
+ with patch("meshchatx.meshchat.generate_ssl_certificate"):
+ app = ReticulumMeshChat(
+ identity=mock_rns_minimal,
+ storage_dir=temp_dir,
+ reticulum_config_dir=temp_dir,
+ )
+
+ handler = None
+ for route in app.get_routes():
+ if route.path == "/api/v1/app/sessions" and route.method == "GET":
+ handler = route.handler
+ break
+ assert handler is not None
+
+ response = await handler(MagicMock())
+ data = json.loads(response.body)
+ assert data["count"] == 0
+ assert data["sessions"] == []
+ assert data["warning"] is False
+ assert data["warning_enabled"] is True
+
+ first = app.active_sessions.add(ip="127.0.0.1", user_agent="A/1")
+ app.active_sessions.add(ip="10.0.0.5", user_agent="B/2")
+ response = await handler(MagicMock())
+ data = json.loads(response.body)
+ assert data["count"] == 2
+ assert data["warning"] is True
+ assert {row["ip"] for row in data["sessions"]} == {"127.0.0.1", "10.0.0.5"}
+ assert {row["user_agent"] for row in data["sessions"]} == {"A/1", "B/2"}
+ assert any(row["id"] == first["id"] for row in data["sessions"])
+
+
+@pytest.mark.asyncio
+async def test_multi_session_warning_setting_in_config_and_payload(
+ mock_rns_minimal,
+ temp_dir,
+):
+ from meshchatx.meshchat import ReticulumMeshChat
+
+ with patch("meshchatx.meshchat.generate_ssl_certificate"):
+ app = ReticulumMeshChat(
+ identity=mock_rns_minimal,
+ storage_dir=temp_dir,
+ reticulum_config_dir=temp_dir,
+ )
+
+ assert app.config.multi_session_warning_enabled.get() is True
+ assert app.get_config_dict()["multi_session_warning_enabled"] is True
+
+ app.active_sessions.add(ip="1.1.1.1", user_agent="one")
+ app.active_sessions.add(ip="2.2.2.2", user_agent="two")
+ payload = app.get_active_sessions_payload()
+ assert payload["warning"] is True
+
+ app.config.multi_session_warning_enabled.set(False)
+ payload = app.get_active_sessions_payload()
+ assert payload["warning_enabled"] is False
+ assert payload["warning"] is False
+
+ app.config.multi_session_warning_enabled.set(True)
+ with (
+ patch.object(app, "send_config_to_websocket_clients", new_callable=AsyncMock),
+ patch.object(
+ app,
+ "send_active_sessions_to_websocket_clients",
+ new_callable=AsyncMock,
+ ) as sessions_broadcast,
+ ):
+ await app.update_config({"multi_session_warning_enabled": False})
+ assert app.config.multi_session_warning_enabled.get() is False
+ sessions_broadcast.assert_awaited()
+
+
+@pytest.mark.asyncio
+async def test_detach_active_session_and_broadcast_payload(mock_rns_minimal, temp_dir):
+ from meshchatx.meshchat import ReticulumMeshChat
+
+ with patch("meshchatx.meshchat.generate_ssl_certificate"):
+ app = ReticulumMeshChat(
+ identity=mock_rns_minimal,
+ storage_dir=temp_dir,
+ reticulum_config_dir=temp_dir,
+ )
+
+ entry = app.active_sessions.add(ip="::1", user_agent="UA")
+ client = MagicMock()
+ client._meshchatx_session_id = entry["id"]
+ assert app._detach_active_session(client) is True
+ assert app.active_sessions.count() == 0
+ assert not hasattr(client, "_meshchatx_session_id")
+ assert app._detach_active_session(client) is False
+
+ sent = []
+
+ async def capture(data):
+ sent.append(json.loads(data))
+
+ app.websocket_broadcast = capture
+ app.active_sessions.add(ip="8.8.8.8", user_agent="Chrome")
+ app.active_sessions.add(ip="9.9.9.9", user_agent="Firefox")
+ await app.send_active_sessions_to_websocket_clients()
+ assert len(sent) == 1
+ assert sent[0]["type"] == "app.sessions.updated"
+ assert sent[0]["count"] == 2
+ assert sent[0]["warning"] is True
+
+
+@pytest.mark.asyncio
+async def test_websocket_broadcast_detaches_dead_session(mock_rns_minimal, temp_dir):
+ from meshchatx.meshchat import ReticulumMeshChat
+
+ with patch("meshchatx.meshchat.generate_ssl_certificate"):
+ app = ReticulumMeshChat(
+ identity=mock_rns_minimal,
+ storage_dir=temp_dir,
+ reticulum_config_dir=temp_dir,
+ )
+
+ live = MagicMock()
+ live.send_str = AsyncMock()
+ live.close = AsyncMock()
+
+ dead = MagicMock()
+ dead.send_str = AsyncMock(side_effect=RuntimeError("gone"))
+ dead.close = AsyncMock()
+ dead_entry = app.active_sessions.add(ip="10.0.0.9", user_agent="Dead/1")
+ dead._meshchatx_session_id = dead_entry["id"]
+ live_entry = app.active_sessions.add(ip="10.0.0.8", user_agent="Live/1")
+ live._meshchatx_session_id = live_entry["id"]
+
+ app.websocket_clients = [live, dead]
+ # Avoid recursive session broadcast while asserting detach bookkeeping.
+ app.send_active_sessions_to_websocket_clients = AsyncMock()
+ await app.websocket_broadcast('{"type":"ping"}')
+
+ assert dead not in app.websocket_clients
+ assert live in app.websocket_clients
+ assert app.active_sessions.count() == 1
+ assert app.active_sessions.list_sessions()[0]["id"] == live_entry["id"]
+ app.send_active_sessions_to_websocket_clients.assert_awaited()
+
+
+@pytest.mark.asyncio
+async def test_connect_disconnect_session_lifecycle_unit(mock_rns_minimal, temp_dir):
+ """Simulate the connect and disconnect bookkeeping without a live aiohttp WS."""
+ from meshchatx.meshchat import ReticulumMeshChat
+
+ with patch("meshchatx.meshchat.generate_ssl_certificate"):
+ app = ReticulumMeshChat(
+ identity=mock_rns_minimal,
+ storage_dir=temp_dir,
+ reticulum_config_dir=temp_dir,
+ )
+
+ broadcasts = []
+
+ async def capture(data):
+ broadcasts.append(json.loads(data))
+
+ app.websocket_broadcast = capture
+
+ clients = []
+ for ip, ua in (("127.0.0.1", "One"), ("127.0.0.1", "Two")):
+ client = MagicMock()
+ session = app.active_sessions.add(ip=ip, user_agent=ua)
+ client._meshchatx_session_id = session["id"]
+ app.websocket_clients.append(client)
+ clients.append(client)
+ await app.send_active_sessions_to_websocket_clients()
+
+ assert app.active_sessions.count() == 2
+ assert broadcasts[-1]["warning"] is True
+ assert broadcasts[-1]["count"] == 2
+
+ client = clients.pop()
+ app.websocket_clients.remove(client)
+ app._detach_active_session(client)
+ await app.send_active_sessions_to_websocket_clients()
+ assert app.active_sessions.count() == 1
+ assert broadcasts[-1]["warning"] is False
+ assert broadcasts[-1]["count"] == 1
diff --git a/tests/backend/test_lxmf_cancel_api.py b/tests/backend/test_lxmf_cancel_api.py
index 53c463f7..280accff 100644
--- a/tests/backend/test_lxmf_cancel_api.py
+++ b/tests/backend/test_lxmf_cancel_api.py
@@ -70,3 +70,41 @@ async def test_lxmf_cancel_endpoint_loads_updated_message_from_database(web_canc
web_cancel_app.database.messages.get_lxmf_message_by_hash.assert_called_with(
message_hash,
)
+
+
+@pytest.mark.asyncio
+async def test_lxmf_cancel_inbound_all_endpoint(web_cancel_app):
+ web_cancel_app.message_router.cancel_all_inbound.return_value = 3
+ web_cancel_app.message_router.inbound_resources.return_value = []
+ aio_app = _build_aio_app(web_cancel_app)
+ async with TestClient(TestServer(aio_app)) as client:
+ response = await client.post(
+ "/api/v1/lxmf/propagation-node/cancel-inbound", json={}
+ )
+ assert response.status == 200
+ body = await response.json()
+ assert body["cancelled"] == 3
+ assert body["inbound_delivery_count"] == 0
+
+ web_cancel_app.message_router.cancel_all_inbound.assert_called_once_with()
+
+
+@pytest.mark.asyncio
+async def test_lxmf_cancel_inbound_one_endpoint(web_cancel_app):
+ resource_hash = "ee" * 16
+ web_cancel_app.message_router.cancel_inbound.return_value = True
+ web_cancel_app.message_router.inbound_resources.return_value = []
+ aio_app = _build_aio_app(web_cancel_app)
+ async with TestClient(TestServer(aio_app)) as client:
+ response = await client.post(
+ "/api/v1/lxmf/propagation-node/cancel-inbound",
+ json={"resource_hash": resource_hash},
+ )
+ assert response.status == 200
+ body = await response.json()
+ assert body["cancelled"] == 1
+ assert body["resource_hash"] == resource_hash
+
+ web_cancel_app.message_router.cancel_inbound.assert_called_once_with(
+ bytes.fromhex(resource_hash),
+ )
diff --git a/tests/backend/test_lxmf_inbound_cancel.py b/tests/backend/test_lxmf_inbound_cancel.py
new file mode 100644
index 00000000..2472beef
--- /dev/null
+++ b/tests/backend/test_lxmf_inbound_cancel.py
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: 0BSD
+
+"""Inbound LXMF delivery cancel helpers (LXMF 1.1 / RNS 1.4)."""
+
+from __future__ import annotations
+
+from types import SimpleNamespace
+from unittest.mock import MagicMock
+
+from meshchatx.src.backend.meshchat_utils import (
+ cancel_inbound_deliveries,
+ list_inbound_deliveries,
+)
+
+
+def test_list_inbound_deliveries_empty_without_router():
+ assert list_inbound_deliveries(None) == []
+
+
+def test_list_inbound_deliveries_serializes_active_resources():
+ resource = MagicMock()
+ resource.hash = bytes.fromhex("ab" * 16)
+ resource.get_data_size.return_value = 1024
+ resource.get_transfer_size.return_value = 1100
+ resource.get_progress.return_value = 0.5
+ router = MagicMock()
+ router.inbound_resources.return_value = [resource]
+
+ items = list_inbound_deliveries(router)
+ assert len(items) == 1
+ assert items[0]["hash"] == "ab" * 16
+ assert items[0]["size_bytes"] == 1024
+ assert items[0]["progress"] == 50.0
+
+
+def test_cancel_all_inbound_deliveries():
+ router = MagicMock()
+ router.cancel_all_inbound.return_value = 2
+ result = cancel_inbound_deliveries(router)
+ assert result["ok"] is True
+ assert result["cancelled"] == 2
+ router.cancel_all_inbound.assert_called_once_with()
+
+
+def test_cancel_one_inbound_delivery():
+ router = MagicMock()
+ router.cancel_inbound.return_value = True
+ result = cancel_inbound_deliveries(router, resource_hash="cd" * 16)
+ assert result["ok"] is True
+ assert result["cancelled"] == 1
+ router.cancel_inbound.assert_called_once_with(bytes.fromhex("cd" * 16))
+
+
+def test_cancel_inbound_rejects_bad_hash():
+ router = MagicMock()
+ result = cancel_inbound_deliveries(router, resource_hash="not-hex")
+ assert result["ok"] is False
+ assert result["cancelled"] == 0
+ router.cancel_inbound.assert_not_called()
+
+
+def test_cancel_inbound_unavailable_without_api():
+ router = SimpleNamespace()
+ result = cancel_inbound_deliveries(router)
+ assert result["ok"] is False
+ assert "unavailable" in result["error"]
diff --git a/tests/backend/test_path_jail_oracles.py b/tests/backend/test_path_jail_oracles.py
index b11e8195..9d0f26ba 100644
--- a/tests/backend/test_path_jail_oracles.py
+++ b/tests/backend/test_path_jail_oracles.py
@@ -63,6 +63,37 @@ def test_filesync_rejects_identity_root_and_reserved(tmp_path):
assert ok.endswith("filesync/custom") or ok.endswith("filesync\\custom")
+def test_filesync_manager_resolve_never_leaves_sync_root(tmp_path):
+ storage = tmp_path / "id"
+ storage.mkdir()
+ (storage / "identity").mkdir()
+ outside = tmp_path / "OUTSIDE"
+ outside.mkdir()
+ (outside / "secret.txt").write_text("x", encoding="utf-8")
+ handler = RnsFilesyncHandler(
+ MagicMock(), SimpleNamespace(hash=b"\x22" * 16), str(storage)
+ )
+ sync_root = handler._sync_root()
+ payloads = [
+ "../identity",
+ "../../OUTSIDE/secret.txt",
+ str(outside / "secret.txt"),
+ str(storage / "identity"),
+ "/etc/passwd",
+ "ok/inside.txt",
+ ]
+ for payload in payloads:
+ abspath, err = handler._resolve_manager_path(payload, allow_root=False)
+ if abspath is not None:
+ assert abspath == sync_root or abspath.startswith(sync_root + os.sep)
+ assert err is None
+ else:
+ assert err is not None
+ root_abs, root_err = handler._resolve_manager_path("", allow_root=True)
+ assert root_err is None
+ assert root_abs == sync_root
+
+
def test_restore_rejects_bad_backup_without_wiping_live_db(tmp_path):
db_path = tmp_path / "main.db"
db = Database(str(db_path))
diff --git a/tests/backend/test_rns_filesync_handler.py b/tests/backend/test_rns_filesync_handler.py
index 2f87bf05..6bc5f4da 100644
--- a/tests/backend/test_rns_filesync_handler.py
+++ b/tests/backend/test_rns_filesync_handler.py
@@ -171,3 +171,83 @@ def test_settings_reject_sync_dir_change_while_running(handler):
result = handler.update_settings(sync_directory="/tmp/other")
assert result["ok"] is False
assert "stop filesync" in result["error"]
+
+
+def test_list_tree_while_stopped(handler):
+ sync = handler._sync_directory
+ nested = os.path.join(sync, "docs")
+ os.makedirs(nested, exist_ok=True)
+ with open(os.path.join(sync, "hello.txt"), "w", encoding="utf-8") as handle:
+ handle.write("hi")
+ with open(os.path.join(nested, "note.md"), "w", encoding="utf-8") as handle:
+ handle.write("note")
+
+ root = handler.list_tree()
+ assert root["ok"] is True
+ assert root["current"] == ""
+ names = {entry["name"] for entry in root["entries"]}
+ assert "hello.txt" in names
+ assert "docs" in names
+ assert handler.service is None
+
+ nested_list = handler.list_tree("docs")
+ assert nested_list["ok"] is True
+ assert nested_list["current"] == "docs"
+ assert any(e["name"] == "note.md" for e in nested_list["entries"])
+
+
+def test_manager_upload_mkdir_delete_roundtrip(handler):
+ mk = handler.manager_mkdir("photos")
+ assert mk["ok"] is True
+ assert mk["path"] == "photos"
+
+ uploaded = handler.manager_upload(
+ filename="shot.jpg",
+ data=b"jpeg-bytes",
+ subdir="photos",
+ )
+ assert uploaded["ok"] is True
+ assert uploaded["path"] == "photos/shot.jpg"
+ assert uploaded["size"] == len(b"jpeg-bytes")
+
+ tree = handler.list_tree("photos")
+ assert any(e["name"] == "shot.jpg" for e in tree["entries"])
+
+ content = handler.manager_content("photos/shot.jpg")
+ assert content["ok"] is True
+ assert content["filename"] == "shot.jpg"
+ with open(content["abspath"], "rb") as handle:
+ assert handle.read() == b"jpeg-bytes"
+
+ deleted = handler.manager_delete("photos/shot.jpg")
+ assert deleted["ok"] is True
+ assert not os.path.exists(
+ os.path.join(handler._sync_directory, "photos", "shot.jpg")
+ )
+
+ empty = handler.manager_delete("photos")
+ assert empty["ok"] is True
+
+
+def test_manager_delete_refuses_nonempty_dir(handler):
+ handler.manager_mkdir("keep")
+ handler.manager_upload(filename="a.txt", data=b"x", subdir="keep")
+ result = handler.manager_delete("keep")
+ assert result["ok"] is False
+ assert "not empty" in result["error"]
+ assert os.path.isdir(os.path.join(handler._sync_directory, "keep"))
+
+
+def test_manager_skips_dotfiles_in_tree(handler):
+ sync = handler._sync_directory
+ with open(os.path.join(sync, ".secret"), "w", encoding="utf-8") as handle:
+ handle.write("nope")
+ with open(os.path.join(sync, ".rns-filesync.db"), "w", encoding="utf-8") as handle:
+ handle.write("{}")
+ with open(os.path.join(sync, "visible.txt"), "w", encoding="utf-8") as handle:
+ handle.write("yes")
+ tree = handler.list_tree()
+ names = {e["name"] for e in tree["entries"]}
+ assert "visible.txt" in names
+ assert ".secret" not in names
+ assert ".rns-filesync.db" not in names
diff --git a/tests/backend/test_rns_filesync_security.py b/tests/backend/test_rns_filesync_security.py
index 08f742de..5daa6e80 100644
--- a/tests/backend/test_rns_filesync_security.py
+++ b/tests/backend/test_rns_filesync_security.py
@@ -13,7 +13,10 @@ import pytest
from hypothesis import HealthCheck, given, settings
from hypothesis import strategies as st
-from meshchatx.src.backend.rns_filesync_handler import RnsFilesyncHandler
+from meshchatx.src.backend.rns_filesync_handler import (
+ RnsFilesyncHandler,
+ _is_forbidden_entry_name,
+)
from rns_filesync.paths import PathJailError, normalize_relpath
_TRAVERSAL_PAYLOADS = (
@@ -378,3 +381,158 @@ def test_start_wires_callbacks_and_reuses_host_reticulum(mock_service_cls, handl
assert mock_service_cls.call_args.kwargs["own_reticulum"] is False
assert service.on_error is not None
assert service.on_sync_progress is not None
+
+
+def test_manager_rejects_traversal_payloads(handler, tmp_path):
+ bait = tmp_path / "bait.txt"
+ bait.write_text("do-not-touch", encoding="utf-8")
+ identity_dir = os.path.join(handler.storage_dir, "identity")
+ os.makedirs(identity_dir, exist_ok=True)
+ secret = os.path.join(identity_dir, "secret.key")
+ with open(secret, "w", encoding="utf-8") as handle:
+ handle.write("private")
+
+ payloads = list(_TRAVERSAL_PAYLOADS) + [
+ str(bait),
+ secret,
+ os.path.join(handler.storage_dir, "identity"),
+ os.path.join(handler.storage_dir, "database.db"),
+ os.path.join(handler.storage_dir, "lxmf"),
+ handler.storage_dir,
+ ]
+ for payload in payloads:
+ tree = handler.list_tree(payload if str(payload).strip() else None)
+ if not str(payload).strip():
+ assert tree["ok"] is True
+ continue
+ assert tree["ok"] is False, payload
+
+ assert handler.manager_content(str(payload))["ok"] is False
+ assert handler.manager_delete(str(payload))["ok"] is False
+ assert handler.manager_mkdir(str(payload))["ok"] is False
+ assert (
+ handler.manager_upload(
+ filename="x.txt",
+ data=b"x",
+ subdir=str(payload),
+ )["ok"]
+ is False
+ )
+
+ assert bait.read_text(encoding="utf-8") == "do-not-touch"
+ with open(secret, encoding="utf-8") as handle:
+ assert handle.read() == "private"
+
+
+def test_manager_rejects_cross_identity_paths(tmp_path):
+ storage_a = tmp_path / "id_a"
+ storage_b = tmp_path / "id_b"
+ storage_a.mkdir()
+ storage_b.mkdir()
+ ha = RnsFilesyncHandler(
+ MagicMock(), SimpleNamespace(hash=b"\xaa" * 16), str(storage_a)
+ )
+ hb = RnsFilesyncHandler(
+ MagicMock(), SimpleNamespace(hash=b"\xbb" * 16), str(storage_b)
+ )
+
+ bait = os.path.join(hb._sync_directory, "peer_secret.txt")
+ with open(bait, "w", encoding="utf-8") as handle:
+ handle.write("b-only")
+
+ assert ha.list_tree(bait)["ok"] is False
+ assert ha.manager_content(bait)["ok"] is False
+ assert ha.manager_delete(bait)["ok"] is False
+ assert os.path.isfile(bait)
+ with open(bait, encoding="utf-8") as handle:
+ assert handle.read() == "b-only"
+
+
+@pytest.mark.skipif(os.name == "nt", reason="symlink tests require POSIX")
+def test_manager_rejects_symlink_escape(handler, tmp_path):
+ outside = tmp_path / "outside_secret.txt"
+ outside.write_text("escape-me", encoding="utf-8")
+ link_path = os.path.join(handler._sync_directory, "escape.txt")
+ os.symlink(str(outside), link_path)
+
+ assert handler.list_tree()["ok"] is True
+ names = {e["name"] for e in handler.list_tree()["entries"]}
+ assert "escape.txt" not in names
+
+ assert handler.manager_content("escape.txt")["ok"] is False
+ assert handler.manager_delete("escape.txt")["ok"] is False
+ assert (
+ handler.manager_upload(
+ filename="escape.txt",
+ data=b"overwrite",
+ subdir="",
+ )["ok"]
+ is False
+ )
+ assert outside.read_text(encoding="utf-8") == "escape-me"
+
+
+def test_manager_upload_rejects_malicious_filenames(handler):
+ for name in (
+ ".hidden",
+ ".rns-filesync.db",
+ "",
+ ".",
+ "..",
+ "x\x00y.txt",
+ ):
+ result = handler.manager_upload(filename=name, data=b"x")
+ assert result["ok"] is False, name
+
+ # Path segments in the client filename are stripped to a basename under sync root.
+ escaped = handler.manager_upload(filename="../evil.txt", data=b"safe")
+ assert escaped["ok"] is True
+ assert escaped["path"] == "evil.txt"
+ assert os.path.isfile(os.path.join(handler._sync_directory, "evil.txt"))
+ assert not os.path.exists(os.path.join(handler.storage_dir, "evil.txt"))
+
+
+def test_manager_refuses_delete_sync_root(handler):
+ assert handler.manager_delete("")["ok"] is False
+ assert handler.manager_delete(".")["ok"] is False
+ assert os.path.isdir(handler._sync_directory)
+
+
+@settings(
+ max_examples=60,
+ deadline=None,
+ suppress_health_check=[HealthCheck.too_slow, HealthCheck.function_scoped_fixture],
+)
+@given(
+ path=st.one_of(
+ st.sampled_from(
+ list(_TRAVERSAL_PAYLOADS) + ["ok.txt", "dir/file.bin", "nested/a/b"]
+ ),
+ st.text(min_size=0, max_size=40),
+ ),
+)
+def test_manager_path_oracle(handler, path):
+ """Oracle: manager resolve accepts only normalize_relpath-safe relative paths."""
+ cleaned = str(path or "").strip()
+ expect_ok = False
+ if cleaned and not os.path.isabs(cleaned) and not cleaned.startswith(("/", "\\")):
+ try:
+ safe = normalize_relpath(cleaned)
+ parts = safe.replace("\\", "/").split("/")
+ if not any(_is_forbidden_entry_name(part) for part in parts):
+ expect_ok = True
+ except PathJailError:
+ expect_ok = False
+
+ abspath, err = handler._resolve_manager_path(path, allow_root=False)
+ if expect_ok:
+ # Path may not exist yet. resolve without must_exist should succeed.
+ assert err is None, path
+ assert abspath is not None
+ assert abspath.startswith(handler._sync_root() + os.sep)
+ else:
+ if cleaned == "":
+ assert err == "path is required"
+ else:
+ assert abspath is None
+ assert err is not None
diff --git a/tests/e2e/acceptance-settings-privacy.spec.js b/tests/e2e/acceptance-settings-privacy.spec.js
index d1508793..415a3078 100644
--- a/tests/e2e/acceptance-settings-privacy.spec.js
+++ b/tests/e2e/acceptance-settings-privacy.spec.js
@@ -27,5 +27,8 @@ test.describe("Acceptance: Settings privacy", () => {
await expect(page.getByText("Privacy mode (block external HTTP/HTTPS)", { exact: true }).first()).toBeVisible({
timeout: 20000,
});
+ await expect(page.getByText("Warn when multiple sessions are connected", { exact: true }).first()).toBeVisible({
+ timeout: 20000,
+ });
});
});
diff --git a/tests/e2e/api.spec.js b/tests/e2e/api.spec.js
index 76c74ca5..1734389b 100644
--- a/tests/e2e/api.spec.js
+++ b/tests/e2e/api.spec.js
@@ -13,4 +13,25 @@ test.describe("HTTP API (via Vite proxy)", () => {
expect(body).toHaveProperty("backups");
expect(Array.isArray(body.backups)).toBeTruthy();
});
+
+ test("active sessions list returns JSON with warning fields", async ({ request }) => {
+ const res = await request.get("/api/v1/app/sessions");
+ expect(res.ok()).toBeTruthy();
+ const body = await res.json();
+ expect(body).toHaveProperty("count");
+ expect(body).toHaveProperty("sessions");
+ expect(body).toHaveProperty("warning");
+ expect(body).toHaveProperty("warning_enabled");
+ expect(Array.isArray(body.sessions)).toBeTruthy();
+ expect(body.count).toBeGreaterThanOrEqual(0);
+ expect(body.warning).toBe(body.warning_enabled && body.count >= 2);
+ });
+
+ test("config exposes multi_session_warning_enabled", async ({ request }) => {
+ const res = await request.get("/api/v1/config");
+ expect(res.ok()).toBeTruthy();
+ const body = await res.json();
+ expect(body.config).toHaveProperty("multi_session_warning_enabled");
+ expect(typeof body.config.multi_session_warning_enabled).toBe("boolean");
+ });
});
diff --git a/tests/e2e/smoke.spec.js b/tests/e2e/smoke.spec.js
index 57b608e1..8bbb8475 100644
--- a/tests/e2e/smoke.spec.js
+++ b/tests/e2e/smoke.spec.js
@@ -71,6 +71,22 @@ test.describe("MeshChatX E2E (Vite + Python backend)", () => {
expect(String(body.app_info.version).length).toBeGreaterThan(0);
});
+ test("backend /api/v1/app/sessions returns active session list", async ({ request }) => {
+ const res = await request.get(`${E2E_BACKEND_ORIGIN}/api/v1/app/sessions`);
+ expect(res.ok()).toBeTruthy();
+ const body = await res.json();
+ expect(typeof body.count).toBe("number");
+ expect(Array.isArray(body.sessions)).toBeTruthy();
+ expect(typeof body.warning).toBe("boolean");
+ expect(typeof body.warning_enabled).toBe("boolean");
+ for (const session of body.sessions) {
+ expect(session).toHaveProperty("id");
+ expect(session).toHaveProperty("ip");
+ expect(session).toHaveProperty("user_agent");
+ expect(session).toHaveProperty("connected_at");
+ }
+ });
+
test("document title, shell, and app name in header", async ({ page }) => {
await page.goto("/");
await expect(page).toHaveTitle(/Reticulum MeshChatX/);
@@ -87,6 +103,9 @@ test.describe("MeshChatX E2E (Vite + Python backend)", () => {
await expect(page).toHaveURL(/#\/about/);
await expect(page.getByText("MeshChatX", { exact: true }).first()).toBeVisible({ timeout: 30000 });
await expect(page.locator("#app")).toBeVisible();
+ await expect(page.getByText("Active sessions", { exact: true }).first()).toBeVisible({
+ timeout: 30000,
+ });
});
test("settings route loads profile section", async ({ page }) => {
diff --git a/tests/frontend/AboutPage.test.js b/tests/frontend/AboutPage.test.js
index 14a30473..cc649b63 100644
--- a/tests/frontend/AboutPage.test.js
+++ b/tests/frontend/AboutPage.test.js
@@ -4,6 +4,7 @@ import AboutPage from "@/components/about/AboutPage.vue";
import ElectronUtils from "@/js/ElectronUtils";
import DialogUtils from "@/js/DialogUtils";
import ToastUtils from "@/js/ToastUtils";
+import { dispatchWsEvent } from "@/js/registries/wsEventRegistry.js";
vi.mock("@/js/ToastUtils", () => ({
default: {
@@ -111,12 +112,13 @@ describe("AboutPage.vue", () => {
await wrapper.vm.$nextTick();
expect(axiosMock.get).toHaveBeenCalledWith("/api/v1/app/info");
- expect(axiosMock.get).toHaveBeenCalledWith("/api/v1/config");
+ expect(axiosMock.get).toHaveBeenCalledWith("/api/v1/app/sessions");
expect(wrapper.text()).toContain("about.app_name");
expect(wrapper.text()).toContain("about.tagline_link");
- expect(wrapper.text()).toContain("hash1");
- expect(wrapper.text()).toContain("hash2");
+ expect(wrapper.text()).toContain("about.environment_information");
+ expect(wrapper.text()).toContain("/path/to/config");
+ expect(wrapper.text()).toContain("/path/to/db");
expect(wrapper.text()).toContain("about.dependency_chain");
expect(wrapper.text()).toContain("LXMFy");
@@ -206,13 +208,13 @@ describe("AboutPage.vue", () => {
});
mountAboutPage();
- expect(axiosMock.get).toHaveBeenCalledTimes(5); // info, config, health, snapshots, backups
+ expect(axiosMock.get).toHaveBeenCalledTimes(5); // info, sessions, health, snapshots, backups
vi.advanceTimersByTime(5000);
- expect(axiosMock.get).toHaveBeenCalledTimes(6); // +1 from updateInterval
+ expect(axiosMock.get).toHaveBeenCalledTimes(7); // +info +sessions from updateInterval
vi.advanceTimersByTime(5000);
- expect(axiosMock.get).toHaveBeenCalledTimes(7); // +2 from updateInterval
+ expect(axiosMock.get).toHaveBeenCalledTimes(9); // +info +sessions again
});
it("handles vacuum database action and shows success toast", async () => {
@@ -426,99 +428,6 @@ describe("AboutPage.vue", () => {
expect(wrapper.text()).toContain("about.path_unknown");
});
- it("shows landlock status on Linux when active", async () => {
- axiosMock.get.mockImplementation((url) => {
- if (url === "/api/v1/app/info")
- return Promise.resolve({
- data: {
- app_info: {
- version: "1.0.0",
- host_platform: "linux",
- landlock_requested: true,
- landlock_active: true,
- landlock_kernel_supported: true,
- landlock_auto_enabled: true,
- landlock_disabled_by_env: false,
- },
- },
- });
- if (url === "/api/v1/config") return Promise.resolve({ data: { config: {} } });
- if (url === "/api/v1/database/health") return Promise.resolve({ data: { database: {} } });
- if (url === "/api/v1/database/snapshots") return Promise.resolve({ data: [] });
- return Promise.reject(new Error("Not found"));
- });
-
- const wrapper = mountAboutPage();
- await vi.runOnlyPendingTimers();
- await wrapper.vm.$nextTick();
- await wrapper.vm.$nextTick();
-
- expect(wrapper.text()).toContain("app.landlock_status");
- expect(wrapper.text()).toContain("app.landlock_active");
- expect(wrapper.text()).not.toContain("app.landlock_kernel_unsupported");
- });
-
- it("shows landlock inactive reason on Linux", async () => {
- axiosMock.get.mockImplementation((url) => {
- if (url === "/api/v1/app/info")
- return Promise.resolve({
- data: {
- app_info: {
- version: "1.0.0",
- host_platform: "linux",
- landlock_requested: false,
- landlock_active: false,
- landlock_kernel_supported: false,
- landlock_auto_enabled: false,
- landlock_disabled_by_env: false,
- },
- },
- });
- if (url === "/api/v1/config") return Promise.resolve({ data: { config: {} } });
- if (url === "/api/v1/database/health") return Promise.resolve({ data: { database: {} } });
- if (url === "/api/v1/database/snapshots") return Promise.resolve({ data: [] });
- return Promise.reject(new Error("Not found"));
- });
-
- const wrapper = mountAboutPage();
- await vi.runOnlyPendingTimers();
- await wrapper.vm.$nextTick();
- await wrapper.vm.$nextTick();
-
- expect(wrapper.text()).toContain("app.landlock_inactive");
- expect(wrapper.text()).toContain("app.landlock_kernel_unsupported");
- });
-
- it("hides landlock status on non-Linux platforms", async () => {
- axiosMock.get.mockImplementation((url) => {
- if (url === "/api/v1/app/info")
- return Promise.resolve({
- data: {
- app_info: {
- version: "1.0.0",
- host_platform: "darwin",
- landlock_requested: false,
- landlock_active: false,
- landlock_kernel_supported: false,
- landlock_auto_enabled: false,
- landlock_disabled_by_env: false,
- },
- },
- });
- if (url === "/api/v1/config") return Promise.resolve({ data: { config: {} } });
- if (url === "/api/v1/database/health") return Promise.resolve({ data: { database: {} } });
- if (url === "/api/v1/database/snapshots") return Promise.resolve({ data: [] });
- return Promise.reject(new Error("Not found"));
- });
-
- const wrapper = mountAboutPage();
- await vi.runOnlyPendingTimers();
- await wrapper.vm.$nextTick();
- await wrapper.vm.$nextTick();
-
- expect(wrapper.text()).not.toContain("app.landlock_status");
- });
-
it("shows MeshChatX usage insights from app info", async () => {
axiosMock.get.mockImplementation((url) => {
if (url === "/api/v1/app/info") {
@@ -613,4 +522,81 @@ describe("AboutPage.vue", () => {
expect(wrapper.text()).toContain("about.battery_saver_measures");
expect(wrapper.vm.batterySaverActiveMeasures.length).toBeGreaterThan(0);
});
+
+ it("loads active sessions with IP and user agent and applies websocket updates", async () => {
+ axiosMock.get.mockImplementation((url) => {
+ if (url === "/api/v1/app/info") {
+ return Promise.resolve({ data: { app_info: { version: "1.0.0" } } });
+ }
+ if (url === "/api/v1/config") {
+ return Promise.resolve({ data: { config: {} } });
+ }
+ if (url === "/api/v1/app/sessions") {
+ return Promise.resolve({
+ data: {
+ count: 2,
+ sessions: [
+ {
+ id: "sess-a",
+ ip: "127.0.0.1",
+ user_agent: "Browser/A",
+ connected_at: 1700000000,
+ },
+ {
+ id: "sess-b",
+ ip: "10.0.0.2",
+ user_agent: "Browser/B",
+ connected_at: 1700000001,
+ },
+ ],
+ warning: true,
+ warning_enabled: true,
+ },
+ });
+ }
+ if (url === "/api/v1/database/health") {
+ return Promise.resolve({ data: { database: {} } });
+ }
+ if (url === "/api/v1/database/snapshots") {
+ return Promise.resolve({ data: { snapshots: [], total: 0 } });
+ }
+ if (url === "/api/v1/database/backups") {
+ return Promise.resolve({ data: { backups: [], total: 0 } });
+ }
+ return Promise.reject(new Error("Not found"));
+ });
+
+ const wrapper = mountAboutPage();
+ await vi.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ expect(axiosMock.get).toHaveBeenCalledWith("/api/v1/app/sessions");
+ expect(wrapper.text()).toContain("about.active_sessions");
+ expect(wrapper.text()).toContain("127.0.0.1");
+ expect(wrapper.text()).toContain("10.0.0.2");
+ expect(wrapper.text()).toContain("Browser/A");
+ expect(wrapper.text()).toContain("Browser/B");
+ expect(wrapper.vm.activeSessionCount).toBe(2);
+
+ await dispatchWsEvent("app.sessions.updated", {
+ count: 1,
+ sessions: [
+ {
+ id: "sess-a",
+ ip: "127.0.0.1",
+ user_agent: "Browser/A",
+ connected_at: 1700000000,
+ },
+ ],
+ warning: false,
+ warning_enabled: true,
+ });
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.activeSessionCount).toBe(1);
+ expect(wrapper.text()).toContain("Browser/A");
+ expect(wrapper.text()).not.toContain("Browser/B");
+ expect(wrapper.text()).not.toContain("10.0.0.2");
+ });
});
diff --git a/tests/frontend/AppMultiSessionWarning.test.js b/tests/frontend/AppMultiSessionWarning.test.js
new file mode 100644
index 00000000..d37cc097
--- /dev/null
+++ b/tests/frontend/AppMultiSessionWarning.test.js
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: 0BSD
+
+import { describe, it, expect, vi, beforeEach } from "vitest";
+import App from "../../meshchatx/src/frontend/components/App.vue";
+import ToastUtils from "../../meshchatx/src/frontend/js/ToastUtils";
+
+vi.mock("../../meshchatx/src/frontend/js/ToastUtils", () => ({
+ default: {
+ success: vi.fn(),
+ error: vi.fn(),
+ warning: vi.fn(),
+ loading: vi.fn(),
+ dismiss: vi.fn(),
+ },
+}));
+
+function makeContext(overrides = {}) {
+ return {
+ config: { multi_session_warning_enabled: true },
+ multiSessionWarningActive: false,
+ handleActiveSessionsUpdated: App.methods.handleActiveSessionsUpdated,
+ $t(key, params = {}) {
+ if (key === "app.multi_session_warning") {
+ return `multi ${params.count}`;
+ }
+ return key;
+ },
+ ...overrides,
+ };
+}
+
+describe("App multi-session warning toast", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ it("toasts when two sessions connect and setting is enabled", () => {
+ const ctx = makeContext();
+ ctx.handleActiveSessionsUpdated({ count: 2, warning_enabled: true });
+ expect(ToastUtils.warning).toHaveBeenCalledWith("multi 2");
+ expect(ctx.multiSessionWarningActive).toBe(true);
+ });
+
+ it("does not toast again while still above the threshold", () => {
+ const ctx = makeContext({ multiSessionWarningActive: true });
+ ctx.handleActiveSessionsUpdated({ count: 3, warning_enabled: true });
+ expect(ToastUtils.warning).not.toHaveBeenCalled();
+ expect(ctx.multiSessionWarningActive).toBe(true);
+ });
+
+ it("does not toast when the setting is disabled", () => {
+ const ctx = makeContext({
+ config: { multi_session_warning_enabled: false },
+ });
+ ctx.handleActiveSessionsUpdated({ count: 2, warning_enabled: false });
+ expect(ToastUtils.warning).not.toHaveBeenCalled();
+ expect(ctx.multiSessionWarningActive).toBe(false);
+ });
+
+ it("resets and can toast again after dropping below two sessions", () => {
+ const ctx = makeContext({ multiSessionWarningActive: true });
+ ctx.handleActiveSessionsUpdated({ count: 1, warning_enabled: true });
+ expect(ctx.multiSessionWarningActive).toBe(false);
+ ctx.handleActiveSessionsUpdated({ count: 2, warning_enabled: true });
+ expect(ToastUtils.warning).toHaveBeenCalledTimes(1);
+ expect(ctx.multiSessionWarningActive).toBe(true);
+ });
+
+ it("uses config when warning_enabled is omitted from the payload", () => {
+ const ctx = makeContext({
+ config: { multi_session_warning_enabled: false },
+ });
+ ctx.handleActiveSessionsUpdated({ count: 2 });
+ expect(ToastUtils.warning).not.toHaveBeenCalled();
+ });
+});
diff --git a/tests/frontend/RnsFilesyncPage.test.js b/tests/frontend/RnsFilesyncPage.test.js
index 6ab997f9..ec2d98bc 100644
--- a/tests/frontend/RnsFilesyncPage.test.js
+++ b/tests/frontend/RnsFilesyncPage.test.js
@@ -25,6 +25,18 @@ vi.mock("@/js/ElectronUtils", () => ({
},
}));
+vi.mock("@/js/DialogUtils", () => ({
+ default: {
+ confirm: vi.fn().mockResolvedValue(true),
+ },
+}));
+
+vi.mock("@/js/DownloadUtils", () => ({
+ default: {
+ downloadFromApiResponse: vi.fn().mockResolvedValue(undefined),
+ },
+}));
+
describe("RnsFilesyncPage.vue", () => {
let apiMock;
@@ -33,6 +45,7 @@ describe("RnsFilesyncPage.vue", () => {
get: vi.fn(),
post: vi.fn(),
patch: vi.fn(),
+ delete: vi.fn(),
};
window.api = apiMock;
@@ -53,6 +66,16 @@ describe("RnsFilesyncPage.vue", () => {
if (url === "/api/v1/filesync/peers") {
return Promise.resolve({ data: { peers: [] } });
}
+ if (url === "/api/v1/filesync/tree") {
+ return Promise.resolve({
+ data: {
+ ok: true,
+ current: "",
+ parent: null,
+ entries: [{ name: "hello.txt", path: "hello.txt", type: "file", size: 4 }],
+ },
+ });
+ }
if (url === "/api/v1/filesync/files") {
return Promise.resolve({ data: { files: [] } });
}
@@ -74,6 +97,7 @@ describe("RnsFilesyncPage.vue", () => {
});
apiMock.post.mockResolvedValue({ data: { ok: true } });
apiMock.patch.mockResolvedValue({ data: { ok: true } });
+ apiMock.delete.mockResolvedValue({ data: { ok: true } });
});
afterEach(() => {
@@ -101,6 +125,13 @@ describe("RnsFilesyncPage.vue", () => {
props: ["open", "initialPath"],
emits: ["close", "select"],
},
+ FilesyncFileManager: {
+ template: "<div class='file-manager-stub'></div>",
+ props: ["syncDirectory"],
+ methods: {
+ refresh: vi.fn().mockResolvedValue(undefined),
+ },
+ },
},
},
});
@@ -112,6 +143,14 @@ describe("RnsFilesyncPage.vue", () => {
expect(wrapper.vm.syncDirectory).toBe("/tmp/sync");
});
+ it("files tab mounts file manager", async () => {
+ const wrapper = mountPage();
+ await vi.waitFor(() => expect(wrapper.vm.syncDirectory).toBe("/tmp/sync"));
+ wrapper.vm.activeTab = "files";
+ await wrapper.vm.$nextTick();
+ expect(wrapper.find(".file-manager-stub").exists()).toBe(true);
+ });
+
it("uses themed input-field classes", async () => {
const wrapper = mountPage();
await vi.waitFor(() => expect(wrapper.vm.syncDirectory).toBe("/tmp/sync"));
@@ -259,3 +298,57 @@ describe("FilesyncDirectoryBrowserModal.vue", () => {
expect(wrapper.emitted("close")).toBeTruthy();
});
});
+
+describe("FilesyncFileManager.vue", () => {
+ let apiMock;
+
+ beforeEach(() => {
+ apiMock = {
+ get: vi.fn().mockResolvedValue({
+ data: {
+ ok: true,
+ current: "",
+ parent: null,
+ entries: [
+ { name: "docs", path: "docs", type: "dir" },
+ { name: "a.txt", path: "a.txt", type: "file", size: 3 },
+ ],
+ },
+ }),
+ post: vi.fn().mockResolvedValue({ data: { ok: true, path: "a.txt" } }),
+ delete: vi.fn().mockResolvedValue({ data: { ok: true } }),
+ };
+ window.api = apiMock;
+ });
+
+ afterEach(() => {
+ delete window.api;
+ vi.clearAllMocks();
+ });
+
+ it("loads tree and uploads via window.api", async () => {
+ const { default: FilesyncFileManager } = await import("@/components/filesync/FilesyncFileManager.vue");
+ const wrapper = mount(FilesyncFileManager, {
+ props: { syncDirectory: "/tmp/sync" },
+ global: {
+ mocks: {
+ $t: (key, params) => (params ? `${key}:${JSON.stringify(params)}` : key),
+ },
+ stubs: {
+ MaterialDesignIcon: {
+ template: "<div></div>",
+ props: ["iconName"],
+ },
+ },
+ },
+ });
+ await vi.waitFor(() => expect(apiMock.get).toHaveBeenCalledWith("/api/v1/filesync/tree", expect.any(Object)));
+ expect(wrapper.vm.entries).toHaveLength(2);
+
+ await wrapper.vm.onUploadSelected({
+ target: { files: [new File(["hi"], "hi.txt")], value: "x" },
+ });
+ expect(apiMock.post).toHaveBeenCalledWith("/api/v1/filesync/upload", expect.any(FormData));
+ expect(ToastUtils.success).toHaveBeenCalledWith("rns_filesync.upload_done");
+ });
+});
diff --git a/tests/frontend/activeSessions.test.js b/tests/frontend/activeSessions.test.js
new file mode 100644
index 00000000..2901918e
--- /dev/null
+++ b/tests/frontend/activeSessions.test.js
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: 0BSD
+
+import { describe, it, expect } from "vitest";
+import { shouldWarnMultiSession, shouldShowMultiSessionToast } from "../../meshchatx/src/frontend/js/activeSessions.js";
+
+describe("activeSessions oracles", () => {
+ it("warns only when count is at least two and setting is on", () => {
+ expect(shouldWarnMultiSession(0, true)).toBe(false);
+ expect(shouldWarnMultiSession(1, true)).toBe(false);
+ expect(shouldWarnMultiSession(2, true)).toBe(true);
+ expect(shouldWarnMultiSession(5, true)).toBe(true);
+ expect(shouldWarnMultiSession(2, false)).toBe(false);
+ expect(shouldWarnMultiSession(2, undefined)).toBe(true);
+ expect(shouldWarnMultiSession(Number.NaN, true)).toBe(false);
+ });
+
+ it("toasts once per multi-session episode", () => {
+ expect(shouldShowMultiSessionToast(1, true, false)).toEqual({
+ show: false,
+ warned: false,
+ });
+ expect(shouldShowMultiSessionToast(2, true, false)).toEqual({
+ show: true,
+ warned: true,
+ });
+ expect(shouldShowMultiSessionToast(3, true, true)).toEqual({
+ show: false,
+ warned: true,
+ });
+ expect(shouldShowMultiSessionToast(1, true, true)).toEqual({
+ show: false,
+ warned: false,
+ });
+ expect(shouldShowMultiSessionToast(2, false, false)).toEqual({
+ show: false,
+ warned: false,
+ });
+ });
+});
diff --git a/vendor/README.txt b/vendor/README.txt
index 91ebc844..15f6c650 100644
--- a/vendor/README.txt
+++ b/vendor/README.txt
@@ -2,10 +2,12 @@ Vendored third-party trees shipped inside the reticulum-meshchatx distribution.
lxmfy/
Upstream: https://git.quad4.io/LXMFy/LXMFy
- Bundled revision: d92cfe0e1ad07fbf6928cf2e02438fe9f0d14384
+ Bundled revision: 483b6928ce2e3cdacd415be92d0f38ae13dca651
Declared version (pyproject): see vendor/lxmfy/pyproject.toml
Update: clone default branch, replace vendor/lxmfy (omit .git), align vendor/README
commit above, run poetry lock / uv lock, regenerate THIRD_PARTY_NOTICES if needed.
+ Note: MeshChatX keeps its Landlock ABI hardening in vendor/lxmfy/lxmfy/landlock_sandbox.py
+ (and matching tests) when refreshing from upstream.
rns_filesync/
Upstream: https://github.com/Quad4-Software/RNS-Filesync
diff --git a/vendor/lxmfy/CHANGELOG.md b/vendor/lxmfy/CHANGELOG.md
index 2603177e..abaf7606 100644
--- a/vendor/lxmfy/CHANGELOG.md
+++ b/vendor/lxmfy/CHANGELOG.md
@@ -1,5 +1,44 @@
# Changelog
+## [2.0.1] - 2026-07-21
+
+### Fixes
+- **RNS shared-instance digest rejection**: Bots no longer default the Reticulum config directory to the bot `config_path` when a user/system Reticulum config already exists. LXMFy now discovers `/etc/reticulum`, `~/.config/reticulum`, then `~/.reticulum` (same order as RNS). When a bot must use an isolated config directory, it forces `share_instance = No` so it cannot collide on the default shared-instance RPC socket/ports with NomadNet, Columba, or `rnsd` (`AuthenticationError: digest sent was rejected`).
+- **Opportunistic delivery**: `opportunistic_sending=True` (the default) now actually selects LXMF `OPPORTUNISTIC` packet delivery instead of only toggling propagation-on-fail. This is required for reliable messaging through public TCP/backbone entrypoints where link-based `DIRECT` delivery often fails.
+
+### Tests
+- Added unit/integration coverage for Reticulum config discovery and isolated `share_instance` handling.
+- Added opt-in live LXMF ping/pong test (`LXMFY_LIVE_LXMF=1`) that selects random online TCP/backbone nodes from `directory.rns.recipes`.
+
+### Updates
+- **Dependencies**: RNS `>=1.3.9`.
+
+## [2.0.0] - 2026-07-10
+
+Final feature release of LXMFy!
+
+### Features
+- **Reticulum Relay Chat (RRC)**: CBOR-encoded RRC client support so bots can join hubs as first-class chat participants ([RRC spec](https://rrc.kc1awv.net/), compatible with NomadNet / rrcd).
+ - New `lxmfy.rrc` package: constants, envelope encode/decode/validation (`cbor2`), `RRCClient`, and `RRCManager`.
+ - `BotConfig` options: `rrc_enabled`, `rrc_hubs`, `rrc_rooms`, `rrc_nick`, `rrc_dest_name`, `rrc_auto_reconnect`, `rrc_persist_sessions`.
+ - Bot API: `connect_rrc()`, `disconnect_rrc()`, `@bot.on_rrc`, `bot.rrc` manager, and `rrc_*` event dispatch.
+ - Session behavior: HELLO/WELCOME, JOIN/PART, MSG/NOTICE/ACTION, PING/PONG, ERROR, RESOURCE_ENVELOPE, auto-reconnect with room re-join, client-side hub limit and rate-limit enforcement, pre-WELCOME send guard.
+ - Optional RRC session persistence across restarts (`rrc_persist_sessions`, default on).
+ - New `RRCBot` template and `lxmfy create --template rrc` / `lxmfy run rrc`.
+
+### Fixes
+- **LXMF crash recovery**: Outgoing `persisted_queue` now updates after dequeue, preserves delivery method on restore, keeps failed and deferred restores, requeues on outbound send failure, and flushes on `cleanup()`.
+- **Invalid persisted destinations**: Corrupt or non-hash destinations (for example test-mode leftovers) are dropped on restore instead of looping forever.
+
+### Updates
+- **Dependencies**: RNS `>=1.3.8`, LXMF `>=1.0.1`, CBOR via `cbor2>=5.4.0`.
+- **Defaults**: `message_persistence_enabled` defaults to `True` for crash-safe outgoing queues.
+- **Memory guards**: Bounded outbound queue (`message_queue_size`, default 50) with drop-oldest on overflow, capped persisted queue/content, and RRC caps for tracked nicks, room members, resource expectations, and pending pings.
+
+### Tests
+- Added RRC/CBOR unit, property, persistence, reconnect, limit, resource-envelope, and bot-integration tests.
+- Opt-in live rrcd smoke test via `LXMFY_LIVE_RRC=1`.
+
## [1.6.5] - 2026-07-04
### Features
diff --git a/vendor/lxmfy/CONTRIBUTING.md b/vendor/lxmfy/CONTRIBUTING.md
deleted file mode 100644
index 93a4aaaf..00000000
--- a/vendor/lxmfy/CONTRIBUTING.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Contributing to LXMFy
-
-Patches are the preferred way to contribute. Create your changes locally,
-export a `.patch` file, and send it over Reticulum.
-
-## Generating a Patch
-
-1. Clone or fork the repository and make your changes on a branch.
-2. Stage and commit your work:
- ```bash
- git add -A
- git commit -m "Short description of the change"
- ```
-3. Export the commit(s) as a `.patch` file:
- ```bash
- # Single most recent commit
- git format-patch -1
-
- # Last N commits
- git format-patch -N
-
- # All commits since a branch point
- git format-patch main..HEAD
- ```
- This produces one `.patch` file per commit (e.g. `0001-my-change.patch`).
-
-## Sending the Patch
-
-Send the `.patch` file as an LXMF message over Reticulum to:
-
-```
-7cc8d66b4f6a0e0e49d34af7f6077b5a
-```
-
-You can attach the file using Sideband, Meshchat, MeshchatX, or any LXMF-capable client with attachments support.
-Include a brief description of what the patch does in the message body.
-
-## Patch Guidelines
-
-- Keep patches focused on a single change or fix.
-- Test your changes before exporting.
-
diff --git a/vendor/lxmfy/README.md b/vendor/lxmfy/README.md
index 1dabcfb4..b05cde17 100644
--- a/vendor/lxmfy/README.md
+++ b/vendor/lxmfy/README.md
@@ -4,12 +4,12 @@ Easily create LXMF bots for the Reticulum Network with this extensible framework
[Docs](https://lxmfy.quad4.io)
-## Features
+## Feature
| Category | Key Capabilities |
| :--- | :--- |
| **Core** | Interactive CLI, Command Prefixes, Cron-style Task Scheduler, Middleware & Event Systems |
-| **Connectivity** | Direct Delivery & Propagation Fallback, Auto-Peering, RNS Link Support, Opportunistic Sending |
+| **Connectivity** | Direct Delivery & Propagation Fallback, Auto-Peering, RNS Link Support, Opportunistic Sending, **RRC (Reticulum Relay Chat) hub client** |
| **Security** | Spam Protection, Role-based Permissions, Identity Pinning, Message Signing/Verification, Landlock LSM Filesystem Sandbox (Linux) |
| **NLP** | Local NLP Intent Classification (Offline/Private), Type-hinted Argument Parsing |
| **Extensions** | Python Cogs, External Script Cogs (Bash, Go, C, etc.), Linux Sandboxing (Landlock LSM, `bwrap`/`firejail`) |
@@ -19,7 +19,7 @@ Easily create LXMF bots for the Reticulum Network with this extensible framework
## Installation
-**Requirements:** Python 3.11+, [RNS](https://pypi.org/project/rns/) 1.4.0+, [LXMF](https://pypi.org/project/lxmf/) 1.1.0+ (installed automatically with LXMFy).
+**Requirements:** Python 3.11+, [RNS](https://pypi.org/project/rns/) 1.3.8+, [LXMF](https://pypi.org/project/lxmf/) 1.0.1+, [cbor2](https://pypi.org/project/cbor2/) 5.4.0+ (installed automatically with LXMFy).
There are many ways to install LXMFy, you pick:
@@ -154,6 +154,33 @@ def echo(ctx, message: str):
bot.run()
```
+## RRC (Reticulum Relay Chat)
+
+Bots can join [RRC](https://rrc.kc1awv.net/) hubs as ordinary clients over RNS Links with CBOR envelopes:
+
+```python
+from lxmfy import LXMFBot, RRCMessage
+
+bot = LXMFBot(
+ name="RoomBot",
+ rrc_enabled=True,
+ rrc_hubs=["your_rrc_hub_destination_hash"],
+ rrc_rooms=["lobby"],
+ rrc_nick="RoomBot",
+)
+
+@bot.on_rrc
+def on_rrc(event, client, payload):
+ if event == "msg" and isinstance(payload, RRCMessage) and payload.mention:
+ client.send_message(payload.room, f"Heard you, {payload.nick}")
+
+bot.run()
+```
+
+Or connect at runtime with `bot.connect_rrc(hub_hash, rooms=["lobby"])`.
+
+Hub sessions persist across restarts by default (`rrc_persist_sessions=True`). Outgoing LXMF messages are also persisted by default (`message_persistence_enabled=True`) so a crash mid-queue does not drop them. The outbound queue is bounded (`message_queue_size`, default 50) and drops the oldest message when full.
+
## Propagation Node Configuration
LXMFy supports three modes for propagation node usage:
diff --git a/vendor/lxmfy/TODO.md b/vendor/lxmfy/TODO.md
deleted file mode 100644
index 75681142..00000000
--- a/vendor/lxmfy/TODO.md
+++ /dev/null
@@ -1,4 +0,0 @@
-- LXST Support
-- More template bots
-- Improve NLP
-- Knowledge Graph
\ No newline at end of file
diff --git a/vendor/lxmfy/docs/source/api-reference.rst b/vendor/lxmfy/docs/source/api-reference.rst
index f8e0305d..33a755f8 100644
--- a/vendor/lxmfy/docs/source/api-reference.rst
+++ b/vendor/lxmfy/docs/source/api-reference.rst
@@ -34,7 +34,7 @@ The main bot class that handles message routing, command processing, and bot lif
signature_verification_enabled=False,
require_message_signatures=False,
identity_pinning_enabled=False,
- message_persistence_enabled=False,
+ message_persistence_enabled=True,
dynamic_cogs_enabled=True,
external_cogs_enabled=True,
external_cogs_sandbox_enabled=True,
@@ -44,7 +44,16 @@ The main bot class that handles message routing, command processing, and bot lif
nlp_enabled=False,
nlp_threshold=0.5,
link_support_enabled=False,
- lxmf_commands_enabled=True
+ lxmf_commands_enabled=True,
+ message_queue_size=50,
+ reticulum_config_dir=None, # or LXMFY_RETICULUM_CONFIG_DIR / "~/.reticulum"
+ rrc_enabled=False,
+ rrc_hubs=[],
+ rrc_rooms=[],
+ rrc_nick=None,
+ rrc_dest_name="rrc.hub",
+ rrc_auto_reconnect=True,
+ rrc_persist_sessions=True,
)
Key Methods
@@ -67,6 +76,10 @@ Key Methods
- :code:`on_first_message()`: Decorator for handling first messages from users
- :code:`on_message()`: Decorator for handling all messages (called before command processing)
- :code:`validate()`: Run validation checks on the bot configuration
+- :code:`connect_rrc(hub_hash, rooms=None, nick=None, dest_name=None, auto_reconnect=None)`: Connect to an RRC hub as a client
+- :code:`disconnect_rrc(hub_hash=None)`: Disconnect one or all RRC hub sessions
+- :code:`on_rrc(callback=None)`: Decorator or register handler for RRC events (:code:`handler(event, client, payload)`)
+- :code:`rrc`: :code:`RRCManager` instance for multi-hub sessions
Structured Commands via LXMF Fields
-----------------------------------
@@ -422,12 +435,13 @@ The retry system tracks delivery attempts per destination and automatically retr
Message Persistence
^^^^^^^^^^^^^^^^^^^
-Outgoing messages can be persisted to disk to ensure they are delivered even after a bot restart.
+Outgoing messages can be persisted to disk to ensure they are delivered even after a bot restart. Persistence is enabled by default. The in-memory outbound queue is bounded (:code:`message_queue_size`, default 50) and drops the oldest message when full. Invalid destination hashes are not restored.
.. code-block:: python
bot = LXMFBot(
- message_persistence_enabled=True
+ message_persistence_enabled=True,
+ message_queue_size=50,
)
Message Handlers
@@ -471,6 +485,64 @@ Message handlers are called in this order:
2. General message handlers (registered with :code:`on_message()`)
3. Command processing (if message starts with command prefix)
+Reticulum Relay Chat (RRC)
+--------------------------
+
+Bots can join `RRC <https://rrc.kc1awv.net/>`_ hubs over RNS Links with CBOR envelopes. Package: :code:`lxmfy.rrc`.
+
+BotConfig options
+^^^^^^^^^^^^^^^^^
+
+* :code:`rrc_enabled` (bool, default :code:`False`): Connect configured hubs on startup
+* :code:`rrc_hubs` (list of hex hashes): Hub destination hashes
+* :code:`rrc_rooms` (list of str): Rooms to auto-join after WELCOME
+* :code:`rrc_nick` (str or None): Nickname on HELLO and room messages
+* :code:`rrc_dest_name` (str, default :code:`"rrc.hub"`): Destination name used to build the hub destination
+* :code:`rrc_auto_reconnect` (bool, default :code:`True`): Reconnect after link loss
+* :code:`rrc_persist_sessions` (bool, default :code:`True`): Persist hubs and rooms across restarts
+* :code:`reticulum_config_dir` (str or None): Reticulum config directory. Also set via :code:`LXMFY_RETICULUM_CONFIG_DIR`. Use the same config as MeshChatX (often :code:`~/.reticulum`) so hub announces are visible.
+
+Example
+^^^^^^^
+
+.. code-block:: python
+
+ from lxmfy import LXMFBot, RRCMessage
+
+ bot = LXMFBot(
+ name="RoomBot",
+ reticulum_config_dir="~/.reticulum",
+ rrc_enabled=True,
+ rrc_hubs=["664fc0e8d2e448658e37bb3f34e6c88f"],
+ rrc_rooms=["general"],
+ rrc_nick="RoomBot",
+ )
+
+ @bot.on_rrc
+ def on_rrc(event, client, payload):
+ if event == "msg" and isinstance(payload, RRCMessage) and payload.mention:
+ client.send_message(payload.room, f"Hi {payload.nick}")
+
+ # Runtime API
+ # bot.connect_rrc(hub_hash, rooms=["general"])
+ # bot.rrc.send_message("general", "hello")
+ # bot.rrc.send_notice("general", "notice")
+ # bot.rrc.send_action("general", "waves")
+ # bot.rrc.join("ops")
+ # bot.rrc.part("ops")
+ # bot.rrc.status()
+ # bot.disconnect_rrc()
+
+Exported types
+^^^^^^^^^^^^^^
+
+* :code:`RRCClient`: Single-hub session
+* :code:`RRCManager`: Multi-hub manager (:code:`bot.rrc`)
+* :code:`RRCMessage`: Room event payload (:code:`kind`, :code:`room`, :code:`text`, :code:`nick`, :code:`src`, :code:`mention`, ...)
+* :code:`RRC_VERSION`: Wire protocol version constant
+
+Common events passed to :code:`@bot.on_rrc` handlers include :code:`status`, :code:`welcome`, :code:`joined`, :code:`parted`, :code:`msg`, :code:`notice`, :code:`action`, :code:`motd`, :code:`error`, and :code:`rtt`.
+
Templates
=========
@@ -512,6 +584,23 @@ Reminder bot with SQLite storage:
bot = ReminderBot()
bot.run()
+RRCBot
+------
+
+RRC room bot that joins configured hubs and replies to :code:`@mentions`. Defaults to hub :code:`664fc0e8d2e448658e37bb3f34e6c88f`, room :code:`#general`, and :code:`~/.reticulum` when available.
+
+.. code-block:: python
+
+ from lxmfy.templates import RRCBot
+
+ bot = RRCBot(
+ hubs=["664fc0e8d2e448658e37bb3f34e6c88f"],
+ rooms=["general"],
+ nick="RRCBot",
+ reticulum_config_dir="~/.reticulum",
+ )
+ bot.run()
+
CLI Tools
=========
@@ -524,9 +613,11 @@ The framework provides command-line tools for bot management:
# Create a bot from template
lxmfy create --template echo mybot
+ lxmfy create --template rrc my_rrc_bot
# Run a template bot
lxmfy run echo
+ lxmfy run rrc
# Test signature verification with a message
lxmfy signatures test
diff --git a/vendor/lxmfy/docs/source/creating-bots.rst b/vendor/lxmfy/docs/source/creating-bots.rst
index b5160070..333a7fee 100644
--- a/vendor/lxmfy/docs/source/creating-bots.rst
+++ b/vendor/lxmfy/docs/source/creating-bots.rst
@@ -75,6 +75,12 @@ LXMFy provides several templates for common bot types. You can use the CLI to ge
# Create a cog test bot (tests cog loading features)
lxmfy create --template cogtest my_cog_test_bot
+ # Create an RRC room bot (joins hubs and replies to @mentions)
+ lxmfy create --template rrc my_rrc_bot
+
+ # Or run the template directly
+ lxmfy run rrc
+
Running these commands creates a Python file (e.g., :code:`my_echo_bot.py`) that imports and runs the chosen template. You can then modify the generated file or the template code itself (:code:`lxmfy/templates/...`).
**Example generated file (:code:`my_cog_test_bot.py`):**
@@ -651,3 +657,87 @@ The retry system:
- Retries failed direct deliveries up to :code:`direct_delivery_retries`
- Resets the retry counter on successful delivery
- Logs retry attempts and failures for debugging
+
+Reticulum Relay Chat (RRC)
+--------------------------
+
+LXMFy bots can join `RRC <https://rrc.kc1awv.net/>`_ hubs as ordinary clients over RNS Links using CBOR envelopes. This is compatible with NomadNet and rrcd style hubs (including MeshChatX when it hosts or joins the same hub).
+
+Reticulum config matters
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The bot must use the **same** Reticulum network as the hub. MeshChatX typically uses :code:`~/.reticulum` with backbone or TCP interfaces. The project-local :code:`config/` directory often uses an isolated instance name and AutoInterface only, so hub announces never arrive and you see :code:`Hub identity unknown`.
+
+Prefer one of:
+
+* Set :code:`reticulum_config_dir` to your user config (usually :code:`~/.reticulum`)
+* Or export :code:`LXMFY_RETICULUM_CONFIG_DIR=~/.reticulum`
+* Keep MeshChatX or :code:`rnsd` running so the shared instance is up before the bot starts
+
+The :code:`rrc` template defaults to :code:`~/.reticulum` when that directory exists.
+
+Quick start with the template
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: bash
+
+ lxmfy run rrc
+
+Defaults:
+
+* Hub: :code:`664fc0e8d2e448658e37bb3f34e6c88f`
+* Room: :code:`#general`
+* Reticulum config: :code:`~/.reticulum` (or :code:`LXMFY_RETICULUM_CONFIG_DIR`)
+
+You should see logs for hub connect, welcome, auto-join, and :code:`RRC joined #general`.
+
+Programmatic RRC bot
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+
+ from lxmfy import LXMFBot, RRCMessage
+
+ bot = LXMFBot(
+ name="RoomBot",
+ reticulum_config_dir="~/.reticulum",
+ rrc_enabled=True,
+ rrc_hubs=["your_rrc_hub_destination_hash"],
+ rrc_rooms=["general"],
+ rrc_nick="RoomBot",
+ rrc_auto_reconnect=True,
+ rrc_persist_sessions=True,
+ )
+
+ @bot.on_rrc
+ def on_rrc(event, client, payload):
+ if event == "welcome":
+ bot.logger.info("Welcomed by hub")
+ return
+ if event != "msg" or not isinstance(payload, RRCMessage):
+ return
+ if payload.mention and payload.room:
+ client.send_message(
+ payload.room,
+ f"Heard you, {payload.nick}",
+ )
+
+ bot.run()
+
+Or connect at runtime:
+
+.. code-block:: python
+
+ bot.connect_rrc("hub_destination_hash", rooms=["general"])
+ bot.rrc.send_message("general", "hello room")
+ bot.rrc.send_action("general", "waves")
+ bot.disconnect_rrc()
+
+Session behavior
+^^^^^^^^^^^^^^^^
+
+* HELLO / WELCOME, JOIN / PART, MSG / NOTICE / ACTION, PING / PONG, ERROR, RESOURCE_ENVELOPE
+* Auto-reconnect with room re-join after WELCOME
+* Client-side hub limit and rate-limit enforcement
+* Session persistence across restarts (:code:`rrc_persist_sessions`, default on)
+* Outgoing LXMF queue persistence is separate (:code:`message_persistence_enabled`)
diff --git a/vendor/lxmfy/docs/source/quick-start.rst b/vendor/lxmfy/docs/source/quick-start.rst
index be64139a..86bd667b 100644
--- a/vendor/lxmfy/docs/source/quick-start.rst
+++ b/vendor/lxmfy/docs/source/quick-start.rst
@@ -5,8 +5,9 @@ Prerequisites
-------------
* Python 3.11+
-* Reticulum Network Stack (:code:`pip install rns`, version 1.3.5+)
+* Reticulum Network Stack (:code:`pip install rns`, version 1.3.8+)
* LXMF (:code:`pip install lxmf`, version 1.0.1+; installed automatically with LXMFy)
+* CBOR (:code:`cbor2`, installed automatically, required for RRC)
* LXMFy (:code:`pip install lxmfy` or install from source)
Creating Your First Bot (Using the CLI)
@@ -112,6 +113,13 @@ Once you're comfortable with the basics, explore these advanced features:
* Configure :code:`direct_delivery_retries` in :code:`LXMFBot(...)` for automatic retry before propagation fallback
* Configure :code:`propagation_node` in bot config (or use :code:`bot.set_propagation_node(...)`) to route through a specific LXMF propagation node
+* Outgoing queue persistence is on by default (:code:`message_persistence_enabled=True`) with a bounded queue (:code:`message_queue_size`)
+
+**Reticulum Relay Chat (RRC):**
+
+* Join RRC hubs as a normal client with :code:`rrc_enabled=True` or the :code:`rrc` template
+* Use the same Reticulum config as MeshChatX or your hub (:code:`reticulum_config_dir` or :code:`LXMFY_RETICULUM_CONFIG_DIR`, typically :code:`~/.reticulum`)
+* See the `Creating Bots <creating-bots.html#reticulum-relay-chat-rrc>`_ guide for room bots and hub discovery
**Security:**
diff --git a/vendor/lxmfy/lxmfy/__init__.py b/vendor/lxmfy/lxmfy/__init__.py
index 391cc5e0..e02bd414 100644
--- a/vendor/lxmfy/lxmfy/__init__.py
+++ b/vendor/lxmfy/lxmfy/__init__.py
@@ -20,6 +20,18 @@ from .help import HelpFormatter, HelpSystem
from .lxmf_fields import FIELD_COMMANDS, FIELD_RESULTS, pack_result, unpack_commands
from .middleware import MiddlewareContext, MiddlewareManager, MiddlewareType
from .permissions import DefaultPerms, PermissionManager, Role
+from .rrc import (
+ DEFAULT_DEST_NAME,
+ RRCClient,
+ RRCManager,
+ RRCMessage,
+ RRC_VERSION,
+ decode_envelope,
+ encode_envelope,
+ make_envelope,
+ normalize_room,
+ validate_envelope,
+)
from .scheduler import ScheduledTask, TaskScheduler
from .storage import JSONStorage, SQLiteStorage, Storage
from .validation import format_validation_results, validate_bot
@@ -29,6 +41,7 @@ __all__ = [
"AttachmentType",
"BotConfig",
"Command",
+ "DEFAULT_DEST_NAME",
"DefaultPerms",
"Event",
"EventManager",
@@ -46,19 +59,28 @@ __all__ = [
"MiddlewareType",
"PermissionManager",
"Role",
+ "RRCClient",
+ "RRCManager",
+ "RRCMessage",
+ "RRC_VERSION",
"SQLiteStorage",
"ScheduledTask",
"Storage",
"TaskScheduler",
"__version__",
"command",
+ "decode_envelope",
+ "encode_envelope",
"format_validation_results",
"load_cogs_from_directory",
+ "make_envelope",
+ "normalize_room",
"pack_attachment",
"pack_icon_appearance_field",
"pack_result",
"unpack_commands",
"validate_bot",
+ "validate_envelope",
]
from .__version__ import __version__
diff --git a/vendor/lxmfy/lxmfy/__version__.py b/vendor/lxmfy/lxmfy/__version__.py
index a3685971..16b5b81c 100644
--- a/vendor/lxmfy/lxmfy/__version__.py
+++ b/vendor/lxmfy/lxmfy/__version__.py
@@ -12,6 +12,6 @@ except PackageNotFoundError:
pyproject = tomllib.load(f)
__version__ = pyproject["project"]["version"]
else:
- __version__ = "1.6.2"
+ __version__ = "2.0.1"
except Exception:
- __version__ = "1.6.2"
+ __version__ = "2.0.1"
diff --git a/vendor/lxmfy/lxmfy/cli.py b/vendor/lxmfy/lxmfy/cli.py
index 35a0ec66..679a13e5 100644
--- a/vendor/lxmfy/lxmfy/cli.py
+++ b/vendor/lxmfy/lxmfy/cli.py
@@ -20,7 +20,7 @@ from .colors import (
print_success,
print_warning,
)
-from .templates import CogTestBot, EchoBot, NoteBot, ReminderBot
+from .templates import CogTestBot, EchoBot, NoteBot, ReminderBot, RRCBot
def get_user_choice() -> str:
@@ -50,7 +50,7 @@ def get_bot_name() -> str:
def get_template_choice() -> str:
"""Get template choice from user input."""
- templates = ["basic", "echo", "reminder", "note", "cogtest"]
+ templates = ["basic", "echo", "reminder", "note", "cogtest", "rrc"]
if Colors.is_colors_supported():
print(f"\n{Colors.CYAN}Available templates:{Colors.ENDC}")
for i, template in enumerate(templates, 1):
@@ -62,12 +62,12 @@ def get_template_choice() -> str:
while True:
if Colors.is_colors_supported():
- choice = input(f"\n{Colors.CYAN}Select template (1-5): {Colors.ENDC}")
+ choice = input(f"\n{Colors.CYAN}Select template (1-6): {Colors.ENDC}")
else:
- choice = input("\nSelect template (1-5): ")
- if choice in ["1", "2", "3", "4", "5"]:
+ choice = input("\nSelect template (1-6): ")
+ if choice in ["1", "2", "3", "4", "5", "6"]:
return templates[int(choice) - 1]
- print_error("Invalid choice. Please enter a number between 1 and 5.")
+ print_error("Invalid choice. Please enter a number between 1 and 6.")
def interactive_create() -> None:
@@ -142,6 +142,7 @@ def interactive_run() -> None:
"reminder": ReminderBot,
"note": NoteBot,
"cogtest": CogTestBot,
+ "rrc": RRCBot,
}
BotClass = template_map[template]
@@ -369,6 +370,7 @@ def create_from_template(template_name: str, output_path: str, bot_name: str) ->
"reminder": ReminderBot,
"note": NoteBot,
"cogtest": CogTestBot,
+ "rrc": RRCBot,
}
if template_name not in template_map:
@@ -466,7 +468,7 @@ Examples:
)
parser.add_argument(
"--template",
- choices=["basic", "echo", "reminder", "note", "cogtest"],
+ choices=["basic", "echo", "reminder", "note", "cogtest", "rrc"],
default="basic",
help="Bot template to use for 'create' command (default: basic)",
)
@@ -550,7 +552,7 @@ To add admin rights, edit {bot_path} and add your LXMF hash to the admins list.
template_name = args.name
if not template_name:
print_error(
- "Please specify a template name to run (echo, reminder, note, cogtest)",
+ "Please specify a template name to run (echo, reminder, note, cogtest, rrc)",
)
sys.exit(1)
@@ -559,6 +561,7 @@ To add admin rights, edit {bot_path} and add your LXMF hash to the admins list.
"reminder": ReminderBot,
"note": NoteBot,
"cogtest": CogTestBot,
+ "rrc": RRCBot,
}
if template_name not in template_map:
@@ -572,6 +575,18 @@ To add admin rights, edit {bot_path} and add your LXMF hash to the admins list.
print_header(f"Starting {template_name} Bot")
bot_instance = BotClass()
+ if template_name == "rrc" and hasattr(bot_instance, "bot"):
+ hubs = bot_instance.bot.config.rrc_hubs or []
+ rooms = bot_instance.bot.config.rrc_rooms or []
+ rns_dir = getattr(bot_instance.bot, "reticulum_config_dir", None)
+ print_info(
+ f"RRC hubs: {', '.join(hubs) if hubs else '(none)'}",
+ )
+ print_info(
+ f"RRC rooms: {', '.join('#' + r for r in rooms) if rooms else '(none)'}",
+ )
+ print_info(f"Reticulum config: {rns_dir or '(default)'}")
+
custom_name = args.name_opt
if custom_name:
try:
diff --git a/vendor/lxmfy/lxmfy/config.py b/vendor/lxmfy/lxmfy/config.py
index 6b4a32c5..dd47b19e 100644
--- a/vendor/lxmfy/lxmfy/config.py
+++ b/vendor/lxmfy/lxmfy/config.py
@@ -42,7 +42,7 @@ class BotConfig:
enable_propagation_node (bool): Whether to run this bot as a propagation node. Defaults to False.
message_storage_limit_mb (float): Maximum storage for propagation node messages in megabytes. Only applies when enable_propagation_node is True. Defaults to 500 MB.
config_path (str): The path to the bot configuration directory. If None, defaults to "config" in the current working directory. Defaults to None.
- reticulum_config_dir (str): The Reticulum config directory used for RNS shared instance/auth state. If None, falls back to config_path. Can also be set via LXMFY_RETICULUM_CONFIG_DIR.
+ reticulum_config_dir (str): The Reticulum config directory used for RNS shared instance/auth state. If None, uses LXMFY_RETICULUM_CONFIG_DIR when set, otherwise discovers the user/system Reticulum config (/etc/reticulum, ~/.config/reticulum, ~/.reticulum), and only then falls back to config_path. Isolated bot configs force share_instance=No to avoid RPC digest rejection with NomadNet/Columba.
test_mode (bool): Whether to run in test mode (skips RNS initialization). Defaults to False.
announce_display_name_file (str): Optional filename under config_path whose UTF-8 contents override the bot display name for LXMF delivery announces. If unset, ``bot_display_name.txt`` is read when present. Otherwise ``name`` is used.
@@ -85,7 +85,8 @@ class BotConfig:
announce_display_name_file: str | None = None
test_mode: bool = False
identity_pinning_enabled: bool = False
- message_persistence_enabled: bool = False
+ message_persistence_enabled: bool = True
+ message_queue_size: int = 50
dynamic_cogs_enabled: bool = True
external_cogs_enabled: bool = True
external_cogs_sandbox_enabled: bool = True
@@ -99,6 +100,13 @@ class BotConfig:
link_support_enabled: bool = False
opportunistic_sending: bool = True
lxmf_commands_enabled: bool = True
+ rrc_enabled: bool = False
+ rrc_hubs: list[str] | None = None
+ rrc_rooms: list[str] | None = None
+ rrc_nick: str | None = None
+ rrc_dest_name: str = "rrc.hub"
+ rrc_auto_reconnect: bool = True
+ rrc_persist_sessions: bool = True
def __post_init__(self):
"""Post-initialization to ensure admins is a set."""
@@ -106,6 +114,10 @@ class BotConfig:
self.admins = set()
if self.reticulum_config_dir is None:
self.reticulum_config_dir = os.environ.get("LXMFY_RETICULUM_CONFIG_DIR")
+ if self.rrc_hubs is None:
+ self.rrc_hubs = []
+ if self.rrc_rooms is None:
+ self.rrc_rooms = []
def __str__(self):
"""Return a string representation of the BotConfig object."""
diff --git a/vendor/lxmfy/lxmfy/core.py b/vendor/lxmfy/lxmfy/core.py
index b387082a..e5481ec3 100644
--- a/vendor/lxmfy/lxmfy/core.py
+++ b/vendor/lxmfy/lxmfy/core.py
@@ -13,7 +13,7 @@ import re
import sys
import time
from concurrent.futures import ThreadPoolExecutor
-from queue import Queue
+from queue import Full, Queue
from typing import Any, Callable, cast
from types import SimpleNamespace
@@ -32,9 +32,15 @@ from .middleware import MiddlewareContext, MiddlewareManager, MiddlewareType
from .moderation import SpamProtection
from .nlp import IntentClassifier
from .permissions import DefaultPerms, PermissionManager
+from .reticulum_config import (
+ ensure_isolated_share_instance_disabled,
+ is_isolated_reticulum_dir,
+ resolve_reticulum_config_dir,
+)
from .scheduler import TaskScheduler
from .signatures import SignatureManager, sign_outgoing_message, verify_incoming_message
from .storage import JSONStorage, MemoryStorage, SQLiteStorage, Storage
+from .rrc import DEFAULT_DEST_NAME, RRCManager, RRCMessage
from .transport import Transport
from .validation import format_validation_results, validate_bot
@@ -66,7 +72,8 @@ class LXMFBot:
self.message_handlers = []
self.delivery_callbacks = []
self.receipts = []
- self.queue = Queue(maxsize=50)
+ queue_size = max(1, int(getattr(self.config, "message_queue_size", 50) or 50))
+ self.queue = Queue(maxsize=queue_size)
self.announce_time = 600
self.router = None
self.local = None
@@ -82,13 +89,18 @@ class LXMFBot:
self.config_path = os.path.join(os.getcwd(), "config")
os.makedirs(self.config_path, exist_ok=True)
- if self.config.reticulum_config_dir:
- self.reticulum_config_dir = os.path.abspath(
- os.path.expanduser(self.config.reticulum_config_dir),
- )
+ if self.config.test_mode and not self.config.reticulum_config_dir:
+ self.reticulum_config_dir = os.path.abspath(self.config_path)
else:
- self.reticulum_config_dir = self.config_path
+ self.reticulum_config_dir = resolve_reticulum_config_dir(
+ self.config.reticulum_config_dir,
+ self.config_path,
+ )
os.makedirs(self.reticulum_config_dir, exist_ok=True)
+ if not self.config.test_mode and is_isolated_reticulum_dir(
+ self.reticulum_config_dir, self.config_path
+ ):
+ ensure_isolated_share_instance_disabled(self.reticulum_config_dir)
if self.config.storage_type == "json":
self.storage = Storage(JSONStorage(self.config.storage_path))
@@ -274,6 +286,63 @@ class LXMFBot:
self.link_handlers = []
self.links = {} # {dest_hash: Link}
+ self.rrc_handlers = []
+ self.rrc = RRCManager(
+ identity=self.identity,
+ nick=self.config.rrc_nick or self.config.name,
+ dest_name=self.config.rrc_dest_name or DEFAULT_DEST_NAME,
+ auto_reconnect=self.config.rrc_auto_reconnect,
+ storage=self.storage,
+ persist_sessions=self.config.rrc_persist_sessions,
+ )
+ self.rrc.on_event(self._rrc_event)
+
+ if self.config.rrc_enabled and not self.config.test_mode:
+ config_rooms = list(self.config.rrc_rooms or [])
+ restored = 0
+ if self.config.rrc_persist_sessions:
+ try:
+ restored = self.rrc.restore_sessions()
+ except Exception as e:
+ self.logger.error("Failed to restore RRC sessions: %s", e)
+ for hub in self.config.rrc_hubs or []:
+ try:
+ RNS.log(
+ f"RRC connecting to hub {hub} rooms={config_rooms or ['(none)']}",
+ RNS.LOG_INFO,
+ )
+ self.connect_rrc(hub, rooms=list(config_rooms))
+ except Exception as e:
+ self.logger.error("Failed to connect RRC hub %s: %s", hub, e)
+ RNS.log(f"RRC hub connect failed for {hub}: {e}", RNS.LOG_ERROR)
+ if restored:
+ RNS.log(f"Restored {restored} RRC hub session(s)", RNS.LOG_INFO)
+ # Ensure persisted hubs not listed in config still get config rooms
+ # when their saved room list was empty.
+ if config_rooms:
+ for client in list(self.rrc.clients.values()):
+ with client._lock:
+ planned = (
+ set(client.rooms)
+ | set(client._rejoin_rooms)
+ | set(
+ client._auto_join_rooms,
+ )
+ )
+ if planned:
+ continue
+ try:
+ self.connect_rrc(
+ client.hub_hash.hex(),
+ rooms=list(config_rooms),
+ )
+ except Exception as e:
+ self.logger.error(
+ "Failed to apply RRC rooms to hub %s: %s",
+ client.hub_hash.hex(),
+ e,
+ )
+
self.signature_manager = SignatureManager(
self,
verification_enabled=self.config.signature_verification_enabled,
@@ -794,6 +863,7 @@ class LXMFBot:
lxmf_fields: dict | None = None,
stamp_cost: int | None = None,
opportunistic: bool | None = None,
+ method=None,
):
"""Send a message to a destination, optionally with custom LXMF fields.
@@ -805,26 +875,31 @@ class LXMFBot:
stamp_cost: Optional stamp cost override. If None, uses config.stamp_cost.
opportunistic: Whether to use opportunistic sending (try direct, then prop).
If None, uses config.opportunistic_sending.
+ method: Optional explicit LXMF delivery method override for crash recovery.
"""
if self.config.test_mode:
# In test mode, just queue a mock message
mock_message = SimpleNamespace()
+ try:
+ mock_message.destination_hash = bytes.fromhex(destination)
+ except ValueError:
+ mock_message.destination_hash = destination.encode("utf-8")
mock_message.content = message.encode("utf-8")
mock_message.title = title.encode("utf-8") if title else None
mock_message.fields = lxmf_fields
- self.queue.put(mock_message)
- return
+ mock_message.desired_method = method
+ return self._enqueue_outbound(mock_message)
try:
dest_hash_bytes = bytes.fromhex(destination)
except ValueError:
RNS.log(f"Invalid destination hash format: {destination}", RNS.LOG_ERROR)
- return
+ return False
if len(dest_hash_bytes) != RNS.Reticulum.TRUNCATED_HASHLENGTH // 8:
RNS.log(f"Invalid destination hash length for {destination}", RNS.LOG_ERROR)
- return
+ return False
identity_instance = RNS.Identity.recall(dest_hash_bytes)
if identity_instance is None:
@@ -837,8 +912,7 @@ class LXMFBot:
"Path requested. If the network knows a path, you will receive an announce shortly.",
RNS.LOG_INFO,
)
- return
-
+ return False
lxmf_destination_obj = RNS.Destination(
identity_instance,
RNS.Destination.OUT,
@@ -886,9 +960,16 @@ class LXMFBot:
f"Using propagation for {destination} after {attempts} failed direct attempts",
RNS.LOG_INFO,
)
+ elif is_opportunistic:
+ # Packet delivery without requiring a Link first. Works much more
+ # reliably through public TCP/backbone entrypoints than DIRECT.
+ desired_method = LXMessage.OPPORTUNISTIC
else:
desired_method = LXMessage.DIRECT
+ if method is not None:
+ desired_method = method
+
# Use provided stamp_cost or fall back to config
final_stamp_cost = (
stamp_cost if stamp_cost is not None else self.config.stamp_cost
@@ -936,36 +1017,95 @@ class LXMFBot:
# Sign the message (pass-through for LXMF's built-in signing)
lxm = sign_outgoing_message(self, lxm)
- # Set propagation fallback if enabled
+ # Set propagation fallback if enabled. Applies when starting with
+ # opportunistic or direct delivery and a propagation node is known.
if (
- desired_method == LXMessage.DIRECT
+ desired_method in (LXMessage.DIRECT, LXMessage.OPPORTUNISTIC)
and (self.config.propagation_fallback_enabled or is_opportunistic)
and has_prop_node
):
setattr(lxm, "try_propagation_on_fail", True)
- self.queue.put(lxm)
- self._persist_queue()
+ if not self._enqueue_outbound(lxm):
+ RNS.log(
+ f"Failed to queue message for {destination}: outbound queue full",
+ RNS.LOG_ERROR,
+ )
+ return False
RNS.log(
f"Message queued for {destination} (method: {desired_method}, opportunistic: {is_opportunistic})",
RNS.LOG_DEBUG,
)
+ return True
+
+ @staticmethod
+ def _destination_hash_len() -> int:
+ return RNS.Reticulum.TRUNCATED_HASHLENGTH // 8
+
+ def _is_valid_destination_hex(self, destination: str) -> bool:
+ if not isinstance(destination, str) or not destination:
+ return False
+ try:
+ raw = bytes.fromhex(destination)
+ except ValueError:
+ return False
+ return len(raw) == self._destination_hash_len()
+
+ def _enqueue_outbound(self, lxm) -> bool:
+ """Enqueue an outbound message without blocking. Drops oldest if full."""
+ try:
+ self.queue.put_nowait(lxm)
+ except Full:
+ try:
+ dropped = self.queue.get_nowait()
+ self.logger.warning(
+ "Outbound queue full (max=%s), dropped oldest message",
+ self.queue.maxsize,
+ )
+ del dropped
+ self.queue.put_nowait(lxm)
+ except Exception as e:
+ self.logger.error("Failed to enqueue outbound message: %s", e)
+ return False
+ self._persist_queue()
+ return True
def _persist_queue(self):
"""Persist the outgoing message queue to storage."""
if getattr(self.config, "message_persistence_enabled", False) is not True:
return
- # Persist destination/content/title/fields/method; LXMessage is not trivially serializable.
-
+ max_items = max(1, int(self.queue.maxsize or 50))
+ max_content = 65536
queued_messages = []
for lxm in list(self.queue.queue):
+ if len(queued_messages) >= max_items:
+ break
try:
- msg_data = {
- "destination": RNS.hexrep(lxm.destination_hash, delimit=False),
- "content": lxm.content.decode("utf-8")
+ destination = RNS.hexrep(lxm.destination_hash, delimit=False)
+ if not self._is_valid_destination_hex(destination):
+ self.logger.warning(
+ "Skipping persist for invalid destination hash length: %s",
+ destination,
+ )
+ continue
+ content = (
+ lxm.content.decode("utf-8")
if isinstance(lxm.content, bytes)
- else lxm.content,
+ else lxm.content
+ )
+ if (
+ isinstance(content, str)
+ and len(content.encode("utf-8")) > max_content
+ ):
+ self.logger.warning(
+ "Skipping persist for oversized message to %s",
+ destination,
+ )
+ continue
+ msg_data = {
+ "destination": destination,
+ "content": content,
"title": lxm.title.decode("utf-8")
if isinstance(lxm.title, bytes)
else lxm.title,
@@ -986,21 +1126,58 @@ class LXMFBot:
persisted = self.storage.get("persisted_queue", [])
if not persisted:
return
+ if not isinstance(persisted, list):
+ self.storage.set("persisted_queue", [])
+ return
+
+ max_items = max(1, int(self.queue.maxsize or 50))
+ if len(persisted) > max_items:
+ self.logger.warning(
+ "Truncating persisted queue from %s to %s messages",
+ len(persisted),
+ max_items,
+ )
+ persisted = persisted[-max_items:]
RNS.log(f"Restoring {len(persisted)} messages from persistence", RNS.LOG_INFO)
+ deferred = []
for msg_data in persisted:
+ if not isinstance(msg_data, dict):
+ continue
+ destination = msg_data.get("destination")
+ if not isinstance(destination, str) or not self._is_valid_destination_hex(
+ destination,
+ ):
+ self.logger.warning(
+ "Dropping persisted message with invalid destination: %s",
+ destination,
+ )
+ continue
+ title = msg_data.get("title")
try:
- self.send(
- msg_data["destination"],
+ queued = self.send(
+ destination,
msg_data["content"],
- title=msg_data.get("title"),
+ title=title if isinstance(title, str) else "Reply",
lxmf_fields=msg_data.get("fields"),
+ method=msg_data.get("method"),
)
+ if not queued:
+ deferred.append(msg_data)
except Exception as e:
self.logger.error("Failed to restore message from persistence: %s", e)
+ deferred.append(msg_data)
- # Clear after loading to avoid duplicates if send() fails again
- self.storage.set("persisted_queue", [])
+ # Keep successfully requeued messages on disk until outbound drain.
+ self._persist_queue()
+ if deferred:
+ current = self.storage.get("persisted_queue", [])
+ if not isinstance(current, list):
+ current = []
+ merged = list(current) + deferred
+ if len(merged) > max_items:
+ merged = merged[-max_items:]
+ self.storage.set("persisted_queue", merged)
def send_with_attachment(
self,
@@ -1047,11 +1224,19 @@ class LXMFBot:
# Process outgoing queue with a timeout to prevent hanging
while not self.queue.empty():
try:
- # Non-blocking get with a small timeout for safety
lxm = self.queue.get(block=False)
+ except Exception:
+ break
+ try:
if self.router:
self.router.handle_outbound(lxm)
- except Exception:
+ self._persist_queue()
+ except Exception as e:
+ self.logger.error("Outbound send failed, requeueing: %s", e)
+ if not self._enqueue_outbound(lxm):
+ self.logger.error(
+ "Failed to requeue after outbound error",
+ )
break
time.sleep(delay)
@@ -1096,6 +1281,15 @@ class LXMFBot:
def cleanup(self):
"""Clean up resources."""
RNS.log("Cleaning up LXMFBot...", RNS.LOG_DEBUG)
+ try:
+ self._persist_queue()
+ except Exception as e:
+ self.logger.error("Failed to persist queue during cleanup: %s", e)
+ if hasattr(self, "rrc") and self.rrc:
+ try:
+ self.rrc.shutdown()
+ except Exception as e:
+ self.logger.error("RRC shutdown failed: %s", e)
self.transport.cleanup()
self.thread_pool.shutdown(wait=False)
self.scheduler.stop()
@@ -1349,6 +1543,85 @@ class LXMFBot:
except Exception as e:
self.logger.error("Error in link handler: %s", e)
+ def connect_rrc(
+ self,
+ hub_hash: str,
+ rooms: list[str] | None = None,
+ nick: str | None = None,
+ dest_name: str | None = None,
+ auto_reconnect: bool | None = None,
+ ):
+ """Connect to an RRC hub as a client.
+
+ Args:
+ hub_hash: Hub destination hash as hex.
+ rooms: Optional rooms to join after WELCOME.
+ nick: Optional nickname override for this hub.
+ dest_name: Destination name (default rrc.hub).
+ auto_reconnect: Override auto-reconnect for this session.
+
+ Returns:
+ The RRCClient session.
+
+ """
+ if self.config.test_mode:
+ raise RuntimeError("RRC connections are unavailable in test_mode")
+ return self.rrc.connect(
+ hub_hash,
+ rooms=rooms,
+ nick=nick,
+ dest_name=dest_name,
+ auto_reconnect=auto_reconnect,
+ )
+
+ def disconnect_rrc(self, hub_hash: str | None = None) -> None:
+ """Disconnect one or all RRC hub sessions."""
+ self.rrc.disconnect(hub_hash)
+
+ def on_rrc(self, callback: Callable | None = None):
+ """Register a handler for RRC events.
+
+ Handler signature: ``handler(event, client, payload)``.
+ Payload is an RRCMessage for room events, or a dict for status/welcome.
+ """
+
+ def decorator(func):
+ self.rrc_handlers.append(func)
+ return func
+
+ if callback is not None:
+ return decorator(callback)
+ return decorator
+
+ def _rrc_event(self, event: str, client, payload) -> None:
+ """Fan RRC events to bot handlers and the event manager."""
+ event_data = {
+ "event": event,
+ "hub_hash": client.hub_hash.hex() if client else None,
+ "payload": payload,
+ }
+ if isinstance(payload, RRCMessage):
+ event_data.update(
+ {
+ "kind": payload.kind,
+ "room": payload.room,
+ "text": payload.text,
+ "nick": payload.nick,
+ "src": payload.src.hex() if payload.src else None,
+ "mention": payload.mention,
+ },
+ )
+ try:
+ self.events.dispatch(Event(f"rrc_{event}", event_data))
+ except Exception as e:
+ self.logger.error("Error dispatching RRC event: %s", e)
+
+ for handler in self.rrc_handlers:
+ try:
+ handler(event, client, payload)
+ except Exception as e:
+ self.logger.error("Error in RRC handler: %s", e)
+
def on_first_message(self):
"""Decorator for registering first message handlers"""
diff --git a/vendor/lxmfy/lxmfy/reticulum_config.py b/vendor/lxmfy/lxmfy/reticulum_config.py
new file mode 100644
index 00000000..5729f0c5
--- /dev/null
+++ b/vendor/lxmfy/lxmfy/reticulum_config.py
@@ -0,0 +1,141 @@
+"""Reticulum config discovery and shared-instance collision avoidance."""
+
+from __future__ import annotations
+
+import logging
+import os
+import re
+from pathlib import Path
+
+logger = logging.getLogger(__name__)
+
+_SHARE_INSTANCE_RE = re.compile(
+ r"(?im)^([ \t]*share_instance[ \t]*=[ \t]*)(\S+)([ \t]*)$",
+)
+
+
+def discover_user_reticulum_config_dir(
+ home: str | None = None,
+ *,
+ system_dir: str = "/etc/reticulum",
+) -> str | None:
+ """Return an existing user/system Reticulum config dir, matching RNS order.
+
+ Order:
+ 1. ``system_dir`` (default ``/etc/reticulum``) when ``config`` exists
+ 2. ``~/.config/reticulum`` when ``config`` exists
+ 3. ``~/.reticulum`` when ``config`` exists
+ """
+ user_home = home if home is not None else os.path.expanduser("~")
+ candidates = [
+ system_dir,
+ os.path.join(user_home, ".config", "reticulum"),
+ os.path.join(user_home, ".reticulum"),
+ ]
+ for path in candidates:
+ if os.path.isdir(path) and os.path.isfile(os.path.join(path, "config")):
+ return os.path.abspath(path)
+ return None
+
+
+def resolve_reticulum_config_dir(
+ explicit: str | None,
+ bot_config_path: str,
+ *,
+ environ: dict[str, str] | None = None,
+ home: str | None = None,
+ system_dir: str = "/etc/reticulum",
+) -> str:
+ """Resolve the Reticulum config directory for an LXMFy bot.
+
+ Prefer an explicit path or ``LXMFY_RETICULUM_CONFIG_DIR``, then an existing
+ user/system Reticulum config. Fall back to the bot config directory only
+ when nothing else is available.
+ """
+ env = environ if environ is not None else os.environ
+ if explicit:
+ return os.path.abspath(os.path.expanduser(explicit))
+ env_dir = env.get("LXMFY_RETICULUM_CONFIG_DIR")
+ if env_dir:
+ return os.path.abspath(os.path.expanduser(env_dir))
+ discovered = discover_user_reticulum_config_dir(
+ home=home,
+ system_dir=system_dir,
+ )
+ if discovered:
+ return discovered
+ return os.path.abspath(bot_config_path)
+
+
+def is_isolated_reticulum_dir(reticulum_config_dir: str, bot_config_path: str) -> bool:
+ """True when the bot uses its own config path as the Reticulum config dir."""
+ return os.path.abspath(reticulum_config_dir) == os.path.abspath(bot_config_path)
+
+
+def ensure_isolated_share_instance_disabled(reticulum_config_dir: str) -> bool:
+ """Ensure an isolated Reticulum config does not join the default shared instance.
+
+ Different config directories derive different RPC authkeys. Sharing the default
+ instance ports/sockets then fails with ``AuthenticationError: digest sent was
+ rejected`` (the failure NomadNet/Columba show when an LXMFy bot collides with
+ them). Isolated bot configs must set ``share_instance = No``.
+
+ Returns:
+ True if the config file was created or modified.
+ """
+ config_path = Path(reticulum_config_dir) / "config"
+ os.makedirs(reticulum_config_dir, exist_ok=True)
+
+ if not config_path.is_file():
+ config_path.write_text(
+ "[reticulum]\n"
+ "enable_transport = Yes\n"
+ "share_instance = No\n"
+ "\n"
+ "[logging]\n"
+ "loglevel = 4\n"
+ "\n"
+ "[interfaces]\n",
+ encoding="utf-8",
+ )
+ logger.info(
+ "Created isolated Reticulum config with share_instance=No at %s",
+ config_path,
+ )
+ return True
+
+ text = config_path.read_text(encoding="utf-8")
+ match = _SHARE_INSTANCE_RE.search(text)
+ if match:
+ current = match.group(2).strip().lower()
+ if current in {"no", "false", "0"}:
+ return False
+ updated = _SHARE_INSTANCE_RE.sub(
+ lambda m: f"{m.group(1)}No{m.group(3)}",
+ text,
+ count=1,
+ )
+ config_path.write_text(updated, encoding="utf-8")
+ logger.warning(
+ "Forced share_instance=No in %s to avoid RNS shared-instance "
+ "RPC digest rejection with other apps (NomadNet, Columba, rnsd)",
+ config_path,
+ )
+ return True
+
+ if re.search(r"(?im)^\[reticulum\]\s*$", text):
+ updated = re.sub(
+ r"(?im)^(\[reticulum\]\s*)$",
+ r"\1\nshare_instance = No",
+ text,
+ count=1,
+ )
+ else:
+ updated = "[reticulum]\nshare_instance = No\n\n" + text
+ config_path.write_text(updated, encoding="utf-8")
+ logger.warning(
+ "Added share_instance=No to %s to avoid RNS shared-instance "
+ "RPC digest rejection with other apps (NomadNet, Columba, rnsd)",
+ config_path,
+ )
+ return True
diff --git a/vendor/lxmfy/lxmfy/rrc/__init__.py b/vendor/lxmfy/lxmfy/rrc/__init__.py
new file mode 100644
index 00000000..807e72c4
--- /dev/null
+++ b/vendor/lxmfy/lxmfy/rrc/__init__.py
@@ -0,0 +1,70 @@
+"""Reticulum Relay Chat (RRC) support for LXMFy bots.
+
+Bots are ordinary RRC clients: they open an RNS Link to a hub, send HELLO,
+join rooms, and exchange CBOR-encoded envelopes. See https://rrc.kc1awv.net/
+"""
+
+from .client import RRCClient, RRCMessage
+from .constants import (
+ CLIENT_NAME,
+ CLIENT_VERSION,
+ DEFAULT_DEST_NAME,
+ RRC_VERSION,
+ STATUS_CONNECTED,
+ STATUS_CONNECTING,
+ STATUS_DISCONNECTED,
+ STATUS_FAILED,
+ T_ACTION,
+ T_ERROR,
+ T_HELLO,
+ T_JOIN,
+ T_JOINED,
+ T_MSG,
+ T_NOTICE,
+ T_PART,
+ T_PARTED,
+ T_PING,
+ T_PONG,
+ T_RESOURCE_ENVELOPE,
+ T_WELCOME,
+)
+from .envelope import (
+ decode_envelope,
+ encode_envelope,
+ make_envelope,
+ normalize_room,
+ validate_envelope,
+)
+from .manager import RRCManager
+
+__all__ = [
+ "CLIENT_NAME",
+ "CLIENT_VERSION",
+ "DEFAULT_DEST_NAME",
+ "RRCClient",
+ "RRCManager",
+ "RRCMessage",
+ "RRC_VERSION",
+ "STATUS_CONNECTED",
+ "STATUS_CONNECTING",
+ "STATUS_DISCONNECTED",
+ "STATUS_FAILED",
+ "T_ACTION",
+ "T_ERROR",
+ "T_HELLO",
+ "T_JOIN",
+ "T_JOINED",
+ "T_MSG",
+ "T_NOTICE",
+ "T_PART",
+ "T_PARTED",
+ "T_PING",
+ "T_PONG",
+ "T_RESOURCE_ENVELOPE",
+ "T_WELCOME",
+ "decode_envelope",
+ "encode_envelope",
+ "make_envelope",
+ "normalize_room",
+ "validate_envelope",
+]
diff --git a/vendor/lxmfy/lxmfy/rrc/client.py b/vendor/lxmfy/lxmfy/rrc/client.py
new file mode 100644
index 00000000..00506553
--- /dev/null
+++ b/vendor/lxmfy/lxmfy/rrc/client.py
@@ -0,0 +1,1110 @@
+"""RRC client session for connecting bots to hubs over RNS Links."""
+
+from __future__ import annotations
+
+import hashlib
+import logging
+import os
+import re
+import threading
+import time
+from collections import deque
+from collections.abc import Callable
+from dataclasses import dataclass, field
+from typing import Any
+
+import RNS
+
+from .constants import (
+ B_HELLO_CAPS,
+ B_HELLO_NAME,
+ B_HELLO_VER,
+ B_RES_ENCODING,
+ B_RES_ID,
+ B_RES_KIND,
+ B_RES_SHA256,
+ B_RES_SIZE,
+ B_WELCOME_CAPS,
+ B_WELCOME_HUB,
+ B_WELCOME_LIMITS,
+ B_WELCOME_VER,
+ CAP_ACTION,
+ CAP_RESOURCE_ENVELOPE,
+ CLIENT_NAME,
+ CLIENT_VERSION,
+ DEFAULT_DEST_NAME,
+ DEFAULT_MAX_MSG_BYTES,
+ DEFAULT_MAX_NICK_BYTES,
+ DEFAULT_MAX_ROOM_BYTES,
+ DEFAULT_MAX_ROOMS,
+ DEFAULT_RATE_PER_MINUTE,
+ K_BODY,
+ K_ID,
+ L_MAX_MSG_BODY_BYTES,
+ L_MAX_NICK_BYTES,
+ L_MAX_ROOM_NAME_BYTES,
+ L_MAX_ROOMS_PER_SESSION,
+ L_RATE_LIMIT_MSGS_PER_MINUTE,
+ MAX_MEMBERS_PER_ROOM,
+ MAX_PENDING_PINGS,
+ MAX_RESOURCE_EXPECTATIONS,
+ MAX_TRACKED_NICKS,
+ RES_KIND_MOTD,
+ RES_KIND_NOTICE,
+ STATUS_CONNECTED,
+ STATUS_CONNECTING,
+ STATUS_DISCONNECTED,
+ STATUS_FAILED,
+ STATUS_NAMES,
+ T_ACTION,
+ T_ERROR,
+ T_HELLO,
+ T_JOIN,
+ T_JOINED,
+ T_MSG,
+ T_NOTICE,
+ T_PART,
+ T_PARTED,
+ T_PING,
+ T_PONG,
+ T_RESOURCE_ENVELOPE,
+ T_WELCOME,
+)
+from .envelope import (
+ decode_envelope,
+ encode_envelope,
+ envelope_body,
+ envelope_nick,
+ envelope_room,
+ envelope_src,
+ envelope_type,
+ make_envelope,
+ normalize_room,
+ now_ms,
+ validate_envelope,
+)
+
+logger = logging.getLogger(__name__)
+
+_MENTION_RE_CACHE: dict[str, re.Pattern[str]] = {}
+
+
+def _mention_re(nick: str) -> re.Pattern[str] | None:
+ if not isinstance(nick, str) or not nick:
+ return None
+ pat = _MENTION_RE_CACHE.get(nick)
+ if pat is None:
+ pat = re.compile(
+ r"(?<![A-Za-z0-9_])@" + re.escape(nick) + r"(?![A-Za-z0-9_])",
+ re.IGNORECASE,
+ )
+ if len(_MENTION_RE_CACHE) > 32:
+ _MENTION_RE_CACHE.clear()
+ _MENTION_RE_CACHE[nick] = pat
+ return pat
+
+
+@dataclass
+class RRCMessage:
+ """A decoded room or session event from an RRC hub."""
+
+ kind: str
+ room: str | None
+ src: bytes | None
+ nick: str | None
+ text: str
+ ts: int
+ hub_hash: bytes | None = None
+ mention: bool = False
+ raw: dict[int, Any] = field(default_factory=dict, repr=False)
+
+
+class RRCClient:
+ """Client session for a single RRC hub destination."""
+
+ HELLO_ATTEMPTS = 5
+ HELLO_INTERVAL_S = 3.0
+ CONNECT_TIMEOUT_S = 45.0
+ PATH_RETRY_INTERVAL_S = 5.0
+ RESOURCE_EXPECTATION_TTL_S = 30.0
+ MAX_RESOURCE_BYTES = 262144
+
+ def __init__(
+ self,
+ identity: RNS.Identity,
+ hub_hash: bytes,
+ dest_name: str = DEFAULT_DEST_NAME,
+ nick: str | None = None,
+ auto_reconnect: bool = True,
+ client_name: str = CLIENT_NAME,
+ client_version: str = CLIENT_VERSION,
+ on_event: Callable[[str, RRCClient, Any], None] | None = None,
+ ):
+ if not isinstance(hub_hash, (bytes, bytearray)) or len(hub_hash) != 16:
+ raise ValueError("hub_hash must be a 16-byte destination hash")
+
+ self.identity = identity
+ self.hub_hash = bytes(hub_hash)
+ self.dest_name = dest_name or DEFAULT_DEST_NAME
+ self.nick = nick
+ self.auto_reconnect = bool(auto_reconnect)
+ self.client_name = client_name
+ self.client_version = client_version
+ self.on_event = on_event
+
+ self.link: RNS.Link | None = None
+ self.status = STATUS_DISCONNECTED
+ self.status_text = "Disconnected"
+ self.welcomed = False
+ self.hub_name: str | None = None
+ self.hub_version: str | None = None
+ self.hub_caps: dict = {}
+ self.motd: str | None = None
+
+ self.max_nick_bytes = DEFAULT_MAX_NICK_BYTES
+ self.max_room_name_bytes = DEFAULT_MAX_ROOM_BYTES
+ self.max_msg_body_bytes = DEFAULT_MAX_MSG_BYTES
+ self.max_rooms_per_session = DEFAULT_MAX_ROOMS
+ self.rate_limit_msgs_per_minute = DEFAULT_RATE_PER_MINUTE
+
+ self.rooms: set[str] = set()
+ self.members: dict[str, set[bytes]] = {}
+ self.nicks: dict[bytes, str] = {}
+
+ self._lock = threading.RLock()
+ self._sent_ids: deque[bytes] = deque(maxlen=256)
+ self._pending_joins: set[str] = set()
+ self._pending_parts: set[str] = set()
+ self._pending_pings: dict[bytes, int] = {}
+ self._auto_join_rooms: list[str] = []
+ self._rejoin_rooms: set[str] = set()
+ self._send_timestamps: deque[float] = deque(maxlen=512)
+ self._resource_expectations: dict[bytes, dict[str, Any]] = {}
+
+ self._stop_hello = threading.Event()
+ self._hello_thread: threading.Thread | None = None
+ self._manual_disconnect = False
+ self._reconnect_attempts = 0
+ self._reconnect_timer: threading.Timer | None = None
+ self._announce_handler = None
+ self._register_announce_handler()
+
+ @property
+ def connected(self) -> bool:
+ """True when the session is welcomed and the link is active."""
+ return self.status == STATUS_CONNECTED and self.welcomed
+
+ def _src_hash(self) -> bytes:
+ """Return this client's identity hash as bytes."""
+ src = self.identity.hash
+ if not isinstance(src, (bytes, bytearray)) or len(src) != 16:
+ raise RuntimeError("identity hash unavailable")
+ return bytes(src)
+
+ def set_nick(self, nick: str | None) -> None:
+ """Set the advisory nickname used on outgoing envelopes."""
+ if nick is not None and nick != "":
+ if not isinstance(nick, str):
+ raise ValueError("nick must be a string")
+ if len(nick.encode("utf-8")) > self.max_nick_bytes:
+ raise ValueError("nick too long for hub limit")
+ with self._lock:
+ self.nick = nick if isinstance(nick, str) and nick else None
+
+ def set_auto_join(self, rooms: list[str] | None) -> None:
+ """Configure rooms to join automatically after WELCOME."""
+ with self._lock:
+ self._auto_join_rooms = [
+ normalize_room(r) for r in (rooms or []) if isinstance(r, str)
+ ]
+
+ def _track_nick(self, src: bytes, nick: str) -> None:
+ self.nicks[src] = nick
+ while len(self.nicks) > MAX_TRACKED_NICKS:
+ self.nicks.pop(next(iter(self.nicks)))
+
+ def _track_member(self, room: str, src: bytes) -> None:
+ members = self.members.setdefault(room, set())
+ if src in members:
+ return
+ if len(members) >= MAX_MEMBERS_PER_ROOM:
+ return
+ members.add(src)
+
+ def _remember_resource_expectation(self, rid: bytes, meta: dict[str, Any]) -> None:
+ while len(self._resource_expectations) >= MAX_RESOURCE_EXPECTATIONS:
+ oldest = next(iter(self._resource_expectations))
+ self._resource_expectations.pop(oldest, None)
+ self._resource_expectations[rid] = meta
+
+ def _emit(self, event: str, payload: Any = None) -> None:
+ if self.on_event is None:
+ return
+ try:
+ self.on_event(event, self, payload)
+ except Exception:
+ logger.exception("RRC event handler failed for %s", event)
+
+ def _set_status(self, status: int, text: str | None = None) -> None:
+ self.status = status
+ if text is not None:
+ self.status_text = text
+ label = {
+ STATUS_DISCONNECTED: "disconnected",
+ STATUS_CONNECTING: "connecting",
+ STATUS_CONNECTED: "connected",
+ STATUS_FAILED: "failed",
+ }.get(status, str(status))
+ hub = self.hub_hash.hex()
+ detail = text or label
+ RNS.log(f"RRC [{hub[:12]}…] {detail}", RNS.LOG_INFO)
+ self._emit("status", {"status": status, "text": self.status_text})
+
+ def connect(self) -> None:
+ """Begin connecting to the hub in a background thread."""
+ with self._lock:
+ if self.status in (STATUS_CONNECTING, STATUS_CONNECTED):
+ return
+ self._manual_disconnect = False
+ if self._reconnect_timer is not None:
+ self._reconnect_timer.cancel()
+ self._reconnect_timer = None
+ text = (
+ f"Reconnecting (attempt {self._reconnect_attempts})"
+ if self._reconnect_attempts > 0
+ else "Connecting"
+ )
+ self._set_status(STATUS_CONNECTING, text)
+
+ threading.Thread(target=self._connect_worker, daemon=True).start()
+
+ def _register_announce_handler(self) -> None:
+ """Listen for the hub announce so reconnect can proceed once keys arrive."""
+ if self._announce_handler is not None:
+ return
+ client = self
+
+ class _HubAnnounceHandler:
+ aspect_filter = None
+ receive_path_responses = True
+
+ def received_announce(
+ self,
+ destination_hash,
+ announced_identity,
+ app_data,
+ *args,
+ **kwargs,
+ ):
+ if destination_hash != client.hub_hash:
+ return
+ RNS.log(
+ f"RRC [{client.hub_hash.hex()[:12]}…] hub announce received",
+ RNS.LOG_INFO,
+ )
+ if client.status in (STATUS_FAILED, STATUS_DISCONNECTED):
+ if client.auto_reconnect and not client._manual_disconnect:
+ client.connect()
+
+ self._announce_handler = _HubAnnounceHandler()
+ try:
+ RNS.Transport.register_announce_handler(self._announce_handler)
+ except Exception:
+ logger.debug("RRC announce handler registration failed", exc_info=True)
+ self._announce_handler = None
+
+ def _deregister_announce_handler(self) -> None:
+ handler = self._announce_handler
+ self._announce_handler = None
+ if handler is None:
+ return
+ try:
+ RNS.Transport.deregister_announce_handler(handler)
+ except Exception:
+ logger.debug("RRC announce handler deregistration failed", exc_info=True)
+
+ def _connect_worker(self) -> None:
+ try:
+ timeout_s = self.CONNECT_TIMEOUT_S
+ self._set_status(
+ STATUS_CONNECTING,
+ "Waiting for hub path/announce",
+ )
+
+ # Prefer the blocking path helper when available.
+ path_ok = False
+ try:
+ path_ok = bool(
+ RNS.Transport.await_path(self.hub_hash, timeout=timeout_s),
+ )
+ except Exception:
+ path_ok = False
+
+ if not path_ok:
+ # Fallback: keep requesting while we wait for Identity.recall.
+ deadline = time.monotonic() + timeout_s
+ next_request = 0.0
+ while time.monotonic() < deadline:
+ now = time.monotonic()
+ if now >= next_request:
+ RNS.Transport.request_path(self.hub_hash)
+ next_request = now + self.PATH_RETRY_INTERVAL_S
+ if RNS.Transport.has_path(self.hub_hash):
+ path_ok = True
+ break
+ if RNS.Identity.recall(self.hub_hash) is not None:
+ path_ok = True
+ break
+ time.sleep(0.2)
+
+ hub_identity = RNS.Identity.recall(self.hub_hash)
+ if hub_identity is None:
+ # Path may exist before keys are fully recalled; wait a bit more.
+ deadline = time.monotonic() + min(10.0, timeout_s)
+ while time.monotonic() < deadline:
+ hub_identity = RNS.Identity.recall(self.hub_hash)
+ if hub_identity is not None:
+ break
+ time.sleep(0.2)
+
+ if hub_identity is None:
+ self._set_status(
+ STATUS_FAILED,
+ "Hub identity unknown (waiting for hub announce on this Reticulum network)",
+ )
+ self._maybe_reconnect()
+ return
+
+ app_name, aspects = RNS.Destination.app_and_aspects_from_name(
+ self.dest_name,
+ )
+ hub_dest = RNS.Destination(
+ hub_identity,
+ RNS.Destination.OUT,
+ RNS.Destination.SINGLE,
+ app_name,
+ *aspects,
+ )
+ if hub_dest.hash != self.hub_hash:
+ self._set_status(
+ STATUS_FAILED,
+ f"Hash/destination name mismatch (expected {self.dest_name})",
+ )
+ self._maybe_reconnect()
+ return
+
+ self._set_status(STATUS_CONNECTING, "Opening link to hub")
+ self._stop_hello.clear()
+ link = RNS.Link(
+ hub_dest,
+ established_callback=self._on_established,
+ closed_callback=self._on_closed,
+ )
+ link.set_packet_callback(lambda data, _pkt: self._on_packet(data))
+ with self._lock:
+ self.link = link
+ except Exception as exc:
+ self._set_status(STATUS_FAILED, f"Connect error: {exc}")
+ self._maybe_reconnect()
+
+ def _on_established(self, link: RNS.Link) -> None:
+ try:
+ link.identify(self.identity)
+ except Exception as exc:
+ logger.error("RRC identify failed: %s", exc)
+ try:
+ link.teardown()
+ except Exception:
+ pass
+ return
+
+ try:
+ link.set_resource_strategy(RNS.Link.ACCEPT_APP)
+ link.set_resource_callback(self._resource_advertised)
+ link.set_resource_started_callback(self._resource_advertised)
+ link.set_resource_concluded_callback(self._resource_concluded)
+ except Exception:
+ logger.debug(
+ "RRC resource callbacks unavailable on this link",
+ exc_info=True,
+ )
+
+ self._set_status(STATUS_CONNECTING, "Identified, sending HELLO")
+
+ def hello_loop() -> None:
+ attempts = 0
+ while (
+ not self._stop_hello.is_set()
+ and not self.welcomed
+ and attempts < self.HELLO_ATTEMPTS
+ ):
+ with self._lock:
+ cur_link = self.link
+ if cur_link is None or cur_link.status != RNS.Link.ACTIVE:
+ return
+ try:
+ self._send_hello(cur_link)
+ except Exception as exc:
+ logger.error("RRC HELLO send failed: %s", exc)
+ attempts += 1
+ self._stop_hello.wait(timeout=self.HELLO_INTERVAL_S)
+
+ if not self.welcomed and not self._stop_hello.is_set():
+ self._set_status(STATUS_FAILED, "WELCOME timeout")
+ try:
+ with self._lock:
+ if self.link is not None:
+ self.link.teardown()
+ except Exception:
+ pass
+
+ self._hello_thread = threading.Thread(target=hello_loop, daemon=True)
+ self._hello_thread.start()
+
+ def _send_hello(self, link: RNS.Link) -> None:
+ body = {
+ B_HELLO_NAME: self.client_name,
+ B_HELLO_VER: self.client_version,
+ B_HELLO_CAPS: {
+ CAP_RESOURCE_ENVELOPE: True,
+ CAP_ACTION: True,
+ },
+ }
+ env = make_envelope(T_HELLO, src=self._src_hash(), body=body, nick=self.nick)
+ payload = encode_envelope(env)
+ RNS.Packet(link, payload).send()
+
+ def _reset_session_state(self, *, preserve_rejoin: bool) -> None:
+ with self._lock:
+ if preserve_rejoin:
+ self._rejoin_rooms |= set(self.rooms)
+ else:
+ self._rejoin_rooms.clear()
+ self.link = None
+ self.welcomed = False
+ self.motd = None
+ self.rooms.clear()
+ self.members.clear()
+ self.nicks.clear()
+ self._pending_joins.clear()
+ self._pending_parts.clear()
+ self._pending_pings.clear()
+ self._resource_expectations.clear()
+ self._send_timestamps.clear()
+
+ def _on_closed(self, _link: RNS.Link) -> None:
+ self._stop_hello.set()
+ with self._lock:
+ should_reconnect = self.auto_reconnect and not self._manual_disconnect
+ preserve_rejoin = not self._manual_disconnect
+ self._reset_session_state(preserve_rejoin=preserve_rejoin)
+ self._set_status(STATUS_DISCONNECTED, "Disconnected")
+ if should_reconnect:
+ self._schedule_reconnect()
+
+ def _maybe_reconnect(self) -> None:
+ if self.auto_reconnect and not self._manual_disconnect:
+ self._schedule_reconnect()
+
+ def _schedule_reconnect(self) -> None:
+ with self._lock:
+ self._reconnect_attempts += 1
+ backoff = min(60.0, max(1.0, 2.0 ** min(self._reconnect_attempts, 6)))
+ if self._reconnect_timer is not None:
+ self._reconnect_timer.cancel()
+
+ def fire() -> None:
+ with self._lock:
+ self._reconnect_timer = None
+ if self._manual_disconnect or not self.auto_reconnect:
+ return
+ self.connect()
+
+ self._reconnect_timer = threading.Timer(backoff, fire)
+ self._reconnect_timer.daemon = True
+ self._reconnect_timer.start()
+ self._set_status(
+ STATUS_DISCONNECTED,
+ f"Reconnect in {int(backoff)}s",
+ )
+
+ def disconnect(self) -> None:
+ """Tear down the session and cancel reconnect."""
+ self._stop_hello.set()
+ with self._lock:
+ self._manual_disconnect = True
+ self._reconnect_attempts = 0
+ if self._reconnect_timer is not None:
+ self._reconnect_timer.cancel()
+ self._reconnect_timer = None
+ link = self.link
+ self.link = None
+ if link is not None:
+ try:
+ link.teardown()
+ except Exception:
+ pass
+ self._reset_session_state(preserve_rejoin=False)
+ self._deregister_announce_handler()
+ self._set_status(STATUS_DISCONNECTED, "Disconnected")
+
+ def _packet_would_fit(self, link: RNS.Link, payload: bytes) -> bool:
+ try:
+ pkt = RNS.Packet(link, payload)
+ pkt.pack()
+ return True
+ except Exception:
+ return False
+
+ def _check_rate_limit(self) -> None:
+ limit = self.rate_limit_msgs_per_minute
+ if not isinstance(limit, int) or limit <= 0:
+ return
+ now = time.monotonic()
+ with self._lock:
+ while self._send_timestamps and now - self._send_timestamps[0] > 60.0:
+ self._send_timestamps.popleft()
+ if len(self._send_timestamps) >= limit:
+ raise RuntimeError("client rate limit exceeded")
+ self._send_timestamps.append(now)
+
+ def _send_env(
+ self,
+ env: dict[int, Any],
+ *,
+ allow_pre_welcome: bool = False,
+ ) -> None:
+ msg_type = envelope_type(env)
+ with self._lock:
+ link = self.link
+ if link is None or link.status != RNS.Link.ACTIVE:
+ raise RuntimeError("not connected")
+ if not allow_pre_welcome and not self.welcomed and msg_type != T_PONG:
+ raise RuntimeError("session not welcomed yet")
+ payload = encode_envelope(env)
+ if not self._packet_would_fit(link, payload):
+ raise RuntimeError("message exceeds link MTU")
+ RNS.Packet(link, payload).send()
+
+ def join(self, room: str, key: str | None = None) -> str:
+ """Request membership in a room. Returns the normalized room name."""
+ room_n = normalize_room(room)
+ if len(room_n.encode("utf-8")) > self.max_room_name_bytes:
+ raise ValueError("room name too long for hub limit")
+ with self._lock:
+ projected = len(self.rooms | self._pending_joins | {room_n})
+ if projected > self.max_rooms_per_session:
+ raise RuntimeError("max rooms per session exceeded")
+ self._pending_joins.add(room_n)
+ self._rejoin_rooms.add(room_n)
+ body = key if isinstance(key, str) and key else None
+ env = make_envelope(
+ T_JOIN,
+ src=self._src_hash(),
+ room=room_n,
+ body=body,
+ nick=self.nick,
+ )
+ self._send_env(env)
+ return room_n
+
+ def part(self, room: str) -> str:
+ """Leave a room. Returns the normalized room name."""
+ room_n = normalize_room(room)
+ env = make_envelope(
+ T_PART,
+ src=self._src_hash(),
+ room=room_n,
+ nick=self.nick,
+ )
+ with self._lock:
+ self._pending_parts.add(room_n)
+ self._rejoin_rooms.discard(room_n)
+ try:
+ self._send_env(env)
+ except Exception as exc:
+ logger.warning("RRC PART send failed for #%s: %s", room_n, exc)
+ with self._lock:
+ self._pending_parts.discard(room_n)
+ self._rejoin_rooms.add(room_n)
+ raise
+ with self._lock:
+ self.rooms.discard(room_n)
+ self.members.pop(room_n, None)
+ return room_n
+
+ def send_message(self, room: str, text: str) -> bytes:
+ """Send a MSG to a room. Returns the message id."""
+ return self._send_room_text(T_MSG, room, text, "msg")
+
+ def send_notice(self, room: str, text: str) -> bytes:
+ """Send a NOTICE to a room. Returns the message id."""
+ return self._send_room_text(T_NOTICE, room, text, "notice")
+
+ def send_action(self, room: str, text: str) -> bytes:
+ """Send an ACTION to a room. Returns the message id."""
+ return self._send_room_text(T_ACTION, room, text, "action")
+
+ def _send_room_text(
+ self,
+ msg_type: int,
+ room: str,
+ text: str,
+ kind: str,
+ ) -> bytes:
+ room_n = normalize_room(room)
+ if not isinstance(text, str) or not text.strip():
+ raise ValueError(f"{kind} text must be non-empty")
+ if len(text.encode("utf-8")) > self.max_msg_body_bytes:
+ raise ValueError(f"{kind} too long for hub limit")
+ self._check_rate_limit()
+ env = make_envelope(
+ msg_type,
+ src=self._src_hash(),
+ room=room_n,
+ body=text,
+ nick=self.nick,
+ )
+ mid = env[K_ID]
+ if isinstance(mid, (bytes, bytearray)):
+ self._sent_ids.append(bytes(mid))
+ self._send_env(env)
+ return bytes(mid)
+
+ def send_command(self, text: str, room: str | None = None) -> None:
+ """Send a slash command as a MSG body."""
+ if not isinstance(text, str) or not text.startswith("/"):
+ raise ValueError("command must start with /")
+ room_n = normalize_room(room) if room else None
+ self._check_rate_limit()
+ env = make_envelope(
+ T_MSG,
+ src=self._src_hash(),
+ room=room_n,
+ body=text,
+ nick=self.nick,
+ )
+ self._send_env(env)
+
+ def ping(self) -> bytes:
+ """Send a PING and return the correlation body."""
+ body = os.urandom(8)
+ env = make_envelope(T_PING, src=self._src_hash(), body=body)
+ with self._lock:
+ now = now_ms()
+ self._pending_pings[body] = now
+ expired = [k for k, v in self._pending_pings.items() if now - v > 15000]
+ for key in expired:
+ self._pending_pings.pop(key, None)
+ while len(self._pending_pings) > MAX_PENDING_PINGS:
+ oldest = next(iter(self._pending_pings))
+ self._pending_pings.pop(oldest, None)
+ self._send_env(env)
+ return body
+
+ def _on_packet(self, data: bytes | bytearray) -> None:
+ env = decode_envelope(data)
+ if env is None:
+ logger.debug("RRC decode failed for %d bytes", len(data))
+ self._emit("decode_error", {"size": len(data)})
+ return
+ if not validate_envelope(env):
+ logger.debug("RRC envelope failed validation")
+ self._emit("decode_error", {"reason": "invalid_envelope"})
+ return
+ msg_type = envelope_type(env)
+ if msg_type is None:
+ return
+
+ if msg_type == T_PING:
+ try:
+ pong = make_envelope(
+ T_PONG,
+ src=self._src_hash(),
+ body=env.get(K_BODY),
+ )
+ self._send_env(pong, allow_pre_welcome=True)
+ except Exception:
+ pass
+ return
+
+ if msg_type == T_PONG:
+ body = env.get(K_BODY)
+ if isinstance(body, (bytes, bytearray)):
+ key = bytes(body)
+ with self._lock:
+ sent = self._pending_pings.pop(key, None)
+ if sent is not None:
+ rtt = max(0, now_ms() - sent)
+ self._emit("pong", {"rtt_ms": rtt, "body": key})
+ return
+
+ if msg_type == T_WELCOME:
+ self._handle_welcome(env)
+ return
+
+ if msg_type == T_JOINED:
+ self._handle_joined(env)
+ return
+
+ if msg_type == T_PARTED:
+ self._handle_parted(env)
+ return
+
+ if msg_type in (T_MSG, T_NOTICE, T_ACTION):
+ self._handle_room_content(env, msg_type)
+ return
+
+ if msg_type == T_ERROR:
+ text = envelope_body(env)
+ if not isinstance(text, str):
+ text = "(error)"
+ room = envelope_room(env)
+ with self._lock:
+ if room and room in self._pending_joins:
+ self._pending_joins.discard(room)
+ self.rooms.discard(room)
+ self._rejoin_rooms.discard(room)
+ if room:
+ self._pending_parts.discard(room)
+ msg = RRCMessage(
+ kind="error",
+ room=room,
+ src=None,
+ nick=None,
+ text=text,
+ ts=now_ms(),
+ hub_hash=self.hub_hash,
+ raw=env,
+ )
+ self._emit("error", msg)
+ return
+
+ if msg_type == T_RESOURCE_ENVELOPE:
+ self._handle_resource_envelope(env)
+ return
+
+ def _handle_resource_envelope(self, env: dict[int, Any]) -> None:
+ body = envelope_body(env)
+ if not isinstance(body, dict):
+ return
+ try:
+ rid = body.get(B_RES_ID)
+ kind = body.get(B_RES_KIND)
+ size = body.get(B_RES_SIZE)
+ sha256 = body.get(B_RES_SHA256)
+ encoding = body.get(B_RES_ENCODING)
+ if not isinstance(rid, (bytes, bytearray)):
+ return
+ if not isinstance(kind, str):
+ return
+ if not isinstance(size, int) or size <= 0:
+ return
+ room = envelope_room(env)
+ with self._lock:
+ self._remember_resource_expectation(
+ bytes(rid),
+ {
+ "kind": kind,
+ "size": size,
+ "sha256": bytes(sha256)
+ if isinstance(sha256, (bytes, bytearray))
+ else None,
+ "encoding": encoding if isinstance(encoding, str) else "utf-8",
+ "room": room,
+ "expires": time.monotonic() + self.RESOURCE_EXPECTATION_TTL_S,
+ },
+ )
+ except Exception:
+ logger.debug("RRC resource envelope parse failed", exc_info=True)
+
+ def _resource_advertised(self, resource) -> bool:
+ try:
+ if hasattr(resource, "get_data_size"):
+ size = resource.get_data_size()
+ elif hasattr(resource, "total_size"):
+ size = resource.total_size
+ else:
+ size = getattr(resource, "size", 0)
+ except Exception:
+ return False
+ if not isinstance(size, int) or size <= 0 or size > self.MAX_RESOURCE_BYTES:
+ return False
+ return True
+
+ def _resource_concluded(self, resource) -> None:
+ try:
+ if resource.status != RNS.Resource.COMPLETE:
+ try:
+ if hasattr(resource, "data") and resource.data:
+ resource.data.close()
+ except Exception:
+ pass
+ return
+ data = None
+ try:
+ data = resource.data.read()
+ finally:
+ try:
+ if hasattr(resource, "data") and resource.data:
+ resource.data.close()
+ except Exception:
+ pass
+ if data is None:
+ return
+
+ now = time.monotonic()
+ matched = None
+ with self._lock:
+ expired = [
+ k
+ for k, v in self._resource_expectations.items()
+ if v["expires"] < now
+ ]
+ for key in expired:
+ self._resource_expectations.pop(key, None)
+ for key, exp in list(self._resource_expectations.items()):
+ if exp["size"] == len(data):
+ matched = exp
+ self._resource_expectations.pop(key, None)
+ break
+
+ kind = matched["kind"] if matched else "blob"
+ room = matched["room"] if matched else None
+ encoding = matched["encoding"] if matched else "utf-8"
+ sha = matched["sha256"] if matched else None
+ if sha is not None and hashlib.sha256(data).digest() != sha:
+ logger.warning("RRC resource SHA256 mismatch")
+ return
+ if kind in (RES_KIND_NOTICE, RES_KIND_MOTD):
+ text = data.decode(encoding, errors="replace")
+ if kind == RES_KIND_MOTD:
+ with self._lock:
+ self.motd = text
+ self._emit("motd", text)
+ msg = RRCMessage(
+ kind="notice",
+ room=room,
+ src=None,
+ nick=None,
+ text=text,
+ ts=now_ms(),
+ hub_hash=self.hub_hash,
+ )
+ self._emit("notice", msg)
+ except Exception:
+ logger.exception("RRC resource handling failed")
+
+ def _handle_welcome(self, env: dict[int, Any]) -> None:
+ self.welcomed = True
+ body = envelope_body(env)
+ if isinstance(body, dict):
+ hub_name = body.get(B_WELCOME_HUB)
+ if isinstance(hub_name, str):
+ self.hub_name = hub_name
+ ver = body.get(B_WELCOME_VER)
+ if isinstance(ver, str):
+ self.hub_version = ver
+ caps = body.get(B_WELCOME_CAPS)
+ if isinstance(caps, dict):
+ self.hub_caps = dict(caps)
+ limits = body.get(B_WELCOME_LIMITS)
+ if isinstance(limits, dict):
+ if L_MAX_NICK_BYTES in limits:
+ self.max_nick_bytes = int(limits[L_MAX_NICK_BYTES])
+ if L_MAX_ROOM_NAME_BYTES in limits:
+ self.max_room_name_bytes = int(limits[L_MAX_ROOM_NAME_BYTES])
+ if L_MAX_MSG_BODY_BYTES in limits:
+ self.max_msg_body_bytes = int(limits[L_MAX_MSG_BODY_BYTES])
+ if L_MAX_ROOMS_PER_SESSION in limits:
+ self.max_rooms_per_session = int(limits[L_MAX_ROOMS_PER_SESSION])
+ if L_RATE_LIMIT_MSGS_PER_MINUTE in limits:
+ self.rate_limit_msgs_per_minute = int(
+ limits[L_RATE_LIMIT_MSGS_PER_MINUTE],
+ )
+ with self._lock:
+ self._reconnect_attempts = 0
+ rooms_to_join = sorted(set(self._auto_join_rooms) | set(self._rejoin_rooms))
+ self._set_status(STATUS_CONNECTED, "Connected")
+ self._emit(
+ "welcome", {"hub_name": self.hub_name, "hub_version": self.hub_version}
+ )
+ if rooms_to_join:
+ RNS.log(
+ f"RRC auto-joining rooms: {', '.join('#' + r for r in rooms_to_join)}",
+ RNS.LOG_INFO,
+ )
+ else:
+ RNS.log("RRC welcomed but no rooms configured to join", RNS.LOG_WARNING)
+ for room in rooms_to_join:
+ try:
+ self.join(room)
+ except Exception:
+ logger.exception("Auto-join failed for room %s", room)
+ RNS.log(f"RRC auto-join failed for #{room}", RNS.LOG_ERROR)
+
+ def _handle_joined(self, env: dict[int, Any]) -> None:
+ room = envelope_room(env)
+ if not room:
+ return
+ body = envelope_body(env)
+ joiner_nick = envelope_nick(env)
+ own_hash = self._src_hash()
+ body_hashes = []
+ if isinstance(body, list):
+ body_hashes = [
+ bytes(entry) for entry in body if isinstance(entry, (bytes, bytearray))
+ ]
+
+ with self._lock:
+ self_join = room in self._pending_joins
+ if self_join:
+ self._pending_joins.discard(room)
+ self.rooms.add(room)
+ self._rejoin_rooms.add(room)
+ for hash_bytes in body_hashes:
+ self._track_member(room, hash_bytes)
+ self._track_member(room, own_hash)
+ if (
+ not self_join
+ and isinstance(joiner_nick, str)
+ and joiner_nick
+ and len(body_hashes) == 1
+ ):
+ joiner = body_hashes[0]
+ if joiner != own_hash:
+ self._track_nick(joiner, joiner_nick)
+
+ if self_join:
+ RNS.log(f"RRC joined #{room}", RNS.LOG_INFO)
+
+ msg = RRCMessage(
+ kind="joined",
+ room=room,
+ src=body_hashes[0] if len(body_hashes) == 1 else None,
+ nick=joiner_nick,
+ text=room,
+ ts=now_ms(),
+ hub_hash=self.hub_hash,
+ raw=env,
+ )
+ self._emit("joined", msg)
+
+ def _handle_parted(self, env: dict[int, Any]) -> None:
+ room = envelope_room(env)
+ if not room:
+ return
+ body = envelope_body(env)
+ parter_nick = envelope_nick(env)
+ own_hash = self._src_hash()
+ body_hashes = []
+ if isinstance(body, list):
+ body_hashes = [
+ bytes(entry) for entry in body if isinstance(entry, (bytes, bytearray))
+ ]
+
+ with self._lock:
+ self_part = room in self._pending_parts
+ if self_part:
+ self._pending_parts.discard(room)
+ if (
+ not self_part
+ and isinstance(parter_nick, str)
+ and parter_nick
+ and len(body_hashes) == 1
+ ):
+ parter = body_hashes[0]
+ if parter != own_hash:
+ self.nicks[parter] = parter_nick
+ members = self.members.get(room)
+ if members is not None:
+ for hash_bytes in body_hashes:
+ members.discard(hash_bytes)
+ if self_part:
+ self.rooms.discard(room)
+ self.members.pop(room, None)
+ self._rejoin_rooms.discard(room)
+
+ msg = RRCMessage(
+ kind="parted",
+ room=room,
+ src=body_hashes[0] if len(body_hashes) == 1 else None,
+ nick=parter_nick,
+ text=room,
+ ts=now_ms(),
+ hub_hash=self.hub_hash,
+ raw=env,
+ )
+ self._emit("parted", msg)
+
+ def _handle_room_content(self, env: dict[int, Any], msg_type: int) -> None:
+ body = envelope_body(env)
+ room = envelope_room(env)
+ src = envelope_src(env)
+ nick = envelope_nick(env)
+ mid = env.get(K_ID)
+ own_hash = self._src_hash()
+
+ if src is not None and src == own_hash:
+ if isinstance(mid, (bytes, bytearray)) and bytes(mid) in self._sent_ids:
+ return
+
+ if src is not None and nick:
+ with self._lock:
+ self._track_nick(src, nick)
+ if room:
+ self._track_member(room, src)
+
+ kind = {T_MSG: "msg", T_NOTICE: "notice", T_ACTION: "action"}[msg_type]
+
+ if (
+ msg_type == T_NOTICE
+ and room is None
+ and isinstance(body, str)
+ and body.strip()
+ ):
+ with self._lock:
+ self.motd = body
+ self._emit("motd", body)
+
+ if not isinstance(body, str):
+ return
+
+ mention = False
+ if src != own_hash and self.nick:
+ pat = _mention_re(self.nick)
+ if pat is not None and pat.search(body):
+ mention = True
+
+ msg = RRCMessage(
+ kind=kind,
+ room=room,
+ src=src,
+ nick=nick,
+ text=body,
+ ts=now_ms(),
+ hub_hash=self.hub_hash,
+ mention=mention,
+ raw=env,
+ )
+ self._emit(kind, msg)
+
+ def status_dict(self) -> dict[str, Any]:
+ """Return a serializable status snapshot."""
+ with self._lock:
+ return {
+ "hub_hash": self.hub_hash.hex(),
+ "dest_name": self.dest_name,
+ "status": STATUS_NAMES.get(self.status, str(self.status)),
+ "status_text": self.status_text,
+ "welcomed": self.welcomed,
+ "hub_name": self.hub_name,
+ "hub_version": self.hub_version,
+ "nick": self.nick,
+ "rooms": sorted(self.rooms),
+ "rejoin_rooms": sorted(self._rejoin_rooms),
+ "motd": self.motd,
+ "max_msg_body_bytes": self.max_msg_body_bytes,
+ "max_rooms_per_session": self.max_rooms_per_session,
+ "rate_limit_msgs_per_minute": self.rate_limit_msgs_per_minute,
+ }
diff --git a/vendor/lxmfy/lxmfy/rrc/constants.py b/vendor/lxmfy/lxmfy/rrc/constants.py
new file mode 100644
index 00000000..d46ceca5
--- /dev/null
+++ b/vendor/lxmfy/lxmfy/rrc/constants.py
@@ -0,0 +1,92 @@
+"""RRC protocol constants aligned with the RRC wire encoding and rrcd.
+
+See https://rrc.kc1awv.net/ and https://github.com/kc1awv/rrcd
+"""
+
+RRC_VERSION = 1
+DEFAULT_DEST_NAME = "rrc.hub"
+
+DEFAULT_MAX_NICK_BYTES = 32
+DEFAULT_MAX_ROOM_BYTES = 64
+DEFAULT_MAX_MSG_BYTES = 350
+DEFAULT_MAX_ROOMS = 32
+DEFAULT_RATE_PER_MINUTE = 240
+
+# Client-side memory guards (not wire protocol limits)
+MAX_TRACKED_NICKS = 2048
+MAX_MEMBERS_PER_ROOM = 1024
+MAX_RESOURCE_EXPECTATIONS = 32
+MAX_PENDING_PINGS = 64
+
+CLIENT_NAME = "lxmfy"
+CLIENT_VERSION = "2.0.0"
+
+K_V = 0
+K_T = 1
+K_ID = 2
+K_TS = 3
+K_SRC = 4
+K_ROOM = 5
+K_BODY = 6
+K_NICK = 7
+K_DST = 8
+
+T_HELLO = 1
+T_WELCOME = 2
+
+T_JOIN = 10
+T_JOINED = 11
+T_PART = 12
+T_PARTED = 13
+
+T_MSG = 20
+T_NOTICE = 21
+T_ACTION = 22
+
+T_PING = 30
+T_PONG = 31
+
+T_ERROR = 40
+
+T_RESOURCE_ENVELOPE = 50
+
+B_HELLO_NAME = 0
+B_HELLO_VER = 1
+B_HELLO_CAPS = 2
+
+B_WELCOME_HUB = 0
+B_WELCOME_VER = 1
+B_WELCOME_CAPS = 2
+B_WELCOME_LIMITS = 3
+
+L_MAX_NICK_BYTES = 0
+L_MAX_ROOM_NAME_BYTES = 1
+L_MAX_MSG_BODY_BYTES = 2
+L_MAX_ROOMS_PER_SESSION = 3
+L_RATE_LIMIT_MSGS_PER_MINUTE = 4
+
+CAP_RESOURCE_ENVELOPE = 0
+CAP_ACTION = 1
+CAP_DIRECT_NOTICE = 2
+
+B_RES_ID = 0
+B_RES_KIND = 1
+B_RES_SIZE = 2
+B_RES_SHA256 = 3
+B_RES_ENCODING = 4
+
+RES_KIND_NOTICE = "notice"
+RES_KIND_MOTD = "motd"
+RES_KIND_BLOB = "blob"
+
+STATUS_DISCONNECTED = 0
+STATUS_CONNECTING = 1
+STATUS_CONNECTED = 2
+STATUS_FAILED = 3
+
+STATUS_NAMES = {
+ STATUS_DISCONNECTED: "disconnected",
+ STATUS_CONNECTING: "connecting",
+ STATUS_CONNECTED: "connected",
+ STATUS_FAILED: "failed",
+}
diff --git a/vendor/lxmfy/lxmfy/rrc/envelope.py b/vendor/lxmfy/lxmfy/rrc/envelope.py
new file mode 100644
index 00000000..72145e13
--- /dev/null
+++ b/vendor/lxmfy/lxmfy/rrc/envelope.py
@@ -0,0 +1,149 @@
+"""CBOR envelope helpers for Reticulum Relay Chat."""
+
+from __future__ import annotations
+
+import os
+import time
+from typing import Any
+
+import cbor2
+
+from .constants import (
+ K_BODY,
+ K_ID,
+ K_NICK,
+ K_ROOM,
+ K_SRC,
+ K_T,
+ K_TS,
+ K_V,
+ RRC_VERSION,
+)
+
+
+def now_ms() -> int:
+ """Return milliseconds since the Unix epoch."""
+ return int(time.time() * 1000)
+
+
+def msg_id() -> bytes:
+ """Return an 8-byte cryptographically random message id."""
+ return os.urandom(8)
+
+
+def normalize_room(room: str | None) -> str:
+ """Normalize a room name for case-insensitive matching."""
+ if not isinstance(room, str):
+ raise ValueError("room must be a non-empty string")
+ normalized = room.strip().lower()
+ if not normalized:
+ raise ValueError("room must not be empty")
+ return normalized
+
+
+def make_envelope(
+ msg_type: int,
+ src: bytes,
+ room: str | None = None,
+ body: Any = None,
+ nick: str | None = None,
+ mid: bytes | None = None,
+ ts: int | None = None,
+) -> dict[int, Any]:
+ """Build a canonical RRC envelope map with integer keys."""
+ if not isinstance(src, (bytes, bytearray)) or len(src) != 16:
+ raise ValueError("src must be a 16-byte identity hash")
+ env: dict[int, Any] = {
+ K_V: RRC_VERSION,
+ K_T: int(msg_type),
+ K_ID: mid or msg_id(),
+ K_TS: ts if ts is not None else now_ms(),
+ K_SRC: bytes(src),
+ }
+ if room is not None:
+ env[K_ROOM] = room
+ if body is not None:
+ env[K_BODY] = body
+ if nick is not None and nick != "":
+ env[K_NICK] = nick
+ return env
+
+
+def encode_envelope(env: dict[int, Any]) -> bytes:
+ """Encode an RRC envelope to CBOR bytes."""
+ return cbor2.dumps(env)
+
+
+def decode_envelope(data: bytes | bytearray) -> dict[int, Any] | None:
+ """Decode CBOR bytes into an RRC envelope map.
+
+ Returns None for malformed payloads. Unknown keys are preserved so
+ callers can ignore them per forward-compatibility rules.
+ """
+ try:
+ env = cbor2.loads(bytes(data))
+ except Exception:
+ return None
+ if not isinstance(env, dict):
+ return None
+ return env
+
+
+def envelope_type(env: dict[int, Any]) -> int | None:
+ """Return the message type from an envelope, or None if missing."""
+ value = env.get(K_T)
+ return int(value) if isinstance(value, int) else None
+
+
+def envelope_room(env: dict[int, Any]) -> str | None:
+ """Return a normalized room name from an envelope, if present."""
+ room = env.get(K_ROOM)
+ if not isinstance(room, str) or not room.strip():
+ return None
+ return room.strip().lower()
+
+
+def envelope_src(env: dict[int, Any]) -> bytes | None:
+ """Return the sender identity hash from an envelope, if present."""
+ src = env.get(K_SRC)
+ if isinstance(src, (bytes, bytearray)) and len(src) == 16:
+ return bytes(src)
+ return None
+
+
+def envelope_nick(env: dict[int, Any]) -> str | None:
+ """Return the advisory nickname from an envelope, if present."""
+ nick = env.get(K_NICK)
+ return nick if isinstance(nick, str) and nick else None
+
+
+def envelope_body(env: dict[int, Any]) -> Any:
+ """Return the body field from an envelope."""
+ return env.get(K_BODY)
+
+
+def validate_envelope(env: dict[int, Any]) -> bool:
+ """Return True when required envelope fields are present and well-formed."""
+ if not isinstance(env, dict):
+ return False
+ version = env.get(K_V)
+ if version is not None and version != RRC_VERSION:
+ return False
+ if not isinstance(env.get(K_T), int):
+ return False
+ mid = env.get(K_ID)
+ if not isinstance(mid, (bytes, bytearray)) or len(mid) != 8:
+ return False
+ ts = env.get(K_TS)
+ if ts is not None and not isinstance(ts, int):
+ return False
+ src = env.get(K_SRC)
+ if not isinstance(src, (bytes, bytearray)) or len(src) != 16:
+ return False
+ room = env.get(K_ROOM)
+ if room is not None and not isinstance(room, str):
+ return False
+ nick = env.get(K_NICK)
+ if nick is not None and not isinstance(nick, str):
+ return False
+ return True
diff --git a/vendor/lxmfy/lxmfy/rrc/manager.py b/vendor/lxmfy/lxmfy/rrc/manager.py
new file mode 100644
index 00000000..5114143e
--- /dev/null
+++ b/vendor/lxmfy/lxmfy/rrc/manager.py
@@ -0,0 +1,275 @@
+"""Manage multiple RRC hub sessions for an LXMFy bot."""
+
+from __future__ import annotations
+
+import logging
+from collections.abc import Callable
+from typing import Any
+
+import RNS
+
+from .client import RRCClient
+from .constants import DEFAULT_DEST_NAME
+from .envelope import normalize_room
+
+logger = logging.getLogger(__name__)
+
+EventCallback = Callable[[str, RRCClient, Any], None]
+
+
+class RRCManager:
+ """Owns RRCClient sessions and fans events out to bot handlers."""
+
+ STORAGE_KEY = "rrc_sessions"
+
+ def __init__(
+ self,
+ identity: RNS.Identity | None = None,
+ nick: str | None = None,
+ dest_name: str = DEFAULT_DEST_NAME,
+ auto_reconnect: bool = True,
+ storage=None,
+ persist_sessions: bool = False,
+ ):
+ self.identity = identity
+ self.nick = nick
+ self.dest_name = dest_name or DEFAULT_DEST_NAME
+ self.auto_reconnect = auto_reconnect
+ self.storage = storage
+ self.persist_sessions = bool(persist_sessions)
+ self.clients: dict[bytes, RRCClient] = {}
+ self._handlers: list[EventCallback] = []
+
+ def on_event(self, callback: EventCallback) -> EventCallback:
+ """Register an event callback. Returns the callback for decorator use."""
+ self._handlers.append(callback)
+ return callback
+
+ def _dispatch(self, event: str, client: RRCClient, payload: Any) -> None:
+ for handler in list(self._handlers):
+ try:
+ handler(event, client, payload)
+ except Exception:
+ logger.exception("RRC manager handler failed for %s", event)
+ if event in ("joined", "parted"):
+ self.save_sessions()
+
+ def connect(
+ self,
+ hub_hash: bytes | str,
+ rooms: list[str] | None = None,
+ nick: str | None = None,
+ dest_name: str | None = None,
+ auto_reconnect: bool | None = None,
+ ) -> RRCClient:
+ """Connect to an RRC hub. Returns the client session."""
+ if self.identity is None:
+ raise RuntimeError("RRC manager has no identity")
+
+ if isinstance(hub_hash, str):
+ hub_bytes = bytes.fromhex(hub_hash)
+ else:
+ hub_bytes = bytes(hub_hash)
+
+ existing = self.clients.get(hub_bytes)
+ if existing is not None:
+ if rooms:
+ existing.set_auto_join(rooms)
+ with existing._lock:
+ for room in rooms:
+ if isinstance(room, str):
+ existing._rejoin_rooms.add(normalize_room(room))
+ if nick is not None:
+ existing.set_nick(nick)
+ if not existing.connected:
+ existing.connect()
+ elif rooms:
+ for room in rooms:
+ try:
+ existing.join(room)
+ except Exception:
+ logger.exception("Failed joining room %s", room)
+ self.save_sessions()
+ return existing
+
+ client = RRCClient(
+ identity=self.identity,
+ hub_hash=hub_bytes,
+ dest_name=dest_name or self.dest_name,
+ nick=nick if nick is not None else self.nick,
+ auto_reconnect=(
+ self.auto_reconnect if auto_reconnect is None else auto_reconnect
+ ),
+ on_event=self._dispatch,
+ )
+ if rooms:
+ client.set_auto_join(rooms)
+ with client._lock:
+ for room in rooms:
+ if isinstance(room, str):
+ client._rejoin_rooms.add(normalize_room(room))
+ self.clients[hub_bytes] = client
+ client.connect()
+ self.save_sessions()
+ return client
+
+ def disconnect(self, hub_hash: bytes | str | None = None) -> None:
+ """Disconnect one hub, or all hubs when hub_hash is None."""
+ if hub_hash is None:
+ for client in list(self.clients.values()):
+ client.disconnect()
+ self.clients.clear()
+ self.save_sessions()
+ return
+
+ hub_bytes = (
+ bytes.fromhex(hub_hash) if isinstance(hub_hash, str) else bytes(hub_hash)
+ )
+ client = self.clients.pop(hub_bytes, None)
+ if client is not None:
+ client.disconnect()
+ self.save_sessions()
+
+ def get(self, hub_hash: bytes | str) -> RRCClient | None:
+ """Return a connected client for a hub hash, if present."""
+ hub_bytes = (
+ bytes.fromhex(hub_hash) if isinstance(hub_hash, str) else bytes(hub_hash)
+ )
+ return self.clients.get(hub_bytes)
+
+ def send_message(
+ self,
+ room: str,
+ text: str,
+ hub_hash: bytes | str | None = None,
+ ) -> bytes:
+ """Send a MSG on the first connected hub, or a specific hub."""
+ client = self._require_client(hub_hash)
+ return client.send_message(room, text)
+
+ def send_notice(
+ self,
+ room: str,
+ text: str,
+ hub_hash: bytes | str | None = None,
+ ) -> bytes:
+ """Send a NOTICE on the first connected hub, or a specific hub."""
+ client = self._require_client(hub_hash)
+ return client.send_notice(room, text)
+
+ def send_action(
+ self,
+ room: str,
+ text: str,
+ hub_hash: bytes | str | None = None,
+ ) -> bytes:
+ """Send an ACTION on the first connected hub, or a specific hub."""
+ client = self._require_client(hub_hash)
+ return client.send_action(room, text)
+
+ def join(
+ self,
+ room: str,
+ hub_hash: bytes | str | None = None,
+ ) -> str:
+ """Join a room on the first connected hub, or a specific hub."""
+ client = self._require_client(hub_hash)
+ room_n = client.join(room)
+ self.save_sessions()
+ return room_n
+
+ def part(
+ self,
+ room: str,
+ hub_hash: bytes | str | None = None,
+ ) -> str:
+ """Part a room on the first connected hub, or a specific hub."""
+ client = self._require_client(hub_hash)
+ room_n = client.part(room)
+ self.save_sessions()
+ return room_n
+
+ def _require_client(self, hub_hash: bytes | str | None) -> RRCClient:
+ if hub_hash is not None:
+ client = self.get(hub_hash)
+ if client is None:
+ raise RuntimeError("RRC hub not connected")
+ return client
+ for client in self.clients.values():
+ if client.connected:
+ return client
+ raise RuntimeError("No connected RRC hub")
+
+ def status(self) -> list[dict[str, Any]]:
+ """Return status snapshots for all hub sessions."""
+ return [client.status_dict() for client in self.clients.values()]
+
+ def save_sessions(self) -> None:
+ """Persist hub hashes, rooms, and nick for crash recovery."""
+ if not self.persist_sessions or self.storage is None:
+ return
+ entries = []
+ for client in self.clients.values():
+ with client._lock:
+ rooms = sorted(
+ set(client.rooms)
+ | set(client._rejoin_rooms)
+ | set(client._auto_join_rooms),
+ )
+ entries.append(
+ {
+ "hub_hash": client.hub_hash.hex(),
+ "dest_name": client.dest_name,
+ "nick": client.nick,
+ "rooms": rooms,
+ "auto_reconnect": client.auto_reconnect,
+ },
+ )
+ try:
+ self.storage.set(self.STORAGE_KEY, entries)
+ except Exception:
+ logger.exception("Failed to persist RRC sessions")
+
+ def restore_sessions(self) -> int:
+ """Restore persisted hub sessions. Returns number of hubs reconnected."""
+ if not self.persist_sessions or self.storage is None:
+ return 0
+ entries = self.storage.get(self.STORAGE_KEY, [])
+ if not isinstance(entries, list):
+ return 0
+ restored = 0
+ for entry in entries:
+ if not isinstance(entry, dict):
+ continue
+ hub_hash = entry.get("hub_hash")
+ if not isinstance(hub_hash, str):
+ continue
+ try:
+ self.connect(
+ hub_hash,
+ rooms=entry.get("rooms")
+ if isinstance(entry.get("rooms"), list)
+ else None,
+ nick=entry.get("nick")
+ if isinstance(entry.get("nick"), str)
+ else None,
+ dest_name=entry.get("dest_name")
+ if isinstance(entry.get("dest_name"), str)
+ else None,
+ auto_reconnect=entry.get("auto_reconnect")
+ if isinstance(entry.get("auto_reconnect"), bool)
+ else None,
+ )
+ restored += 1
+ except Exception:
+ logger.exception("Failed to restore RRC hub %s", hub_hash)
+ return restored
+
+ def shutdown(self) -> None:
+ """Persist session state and tear down links without forgetting hubs."""
+ self.save_sessions()
+ for client in list(self.clients.values()):
+ try:
+ client.disconnect()
+ except Exception:
+ logger.exception("RRC client disconnect failed during shutdown")
diff --git a/vendor/lxmfy/lxmfy/templates/__init__.py b/vendor/lxmfy/lxmfy/templates/__init__.py
index 0be030ed..e2591218 100644
--- a/vendor/lxmfy/lxmfy/templates/__init__.py
+++ b/vendor/lxmfy/lxmfy/templates/__init__.py
@@ -7,5 +7,6 @@ from .cog_test_bot import CogTestBot
from .echo_bot import EchoBot
from .note_bot import NoteBot
from .reminder_bot import ReminderBot
+from .rrc_bot import RRCBot
-__all__ = ["CogTestBot", "EchoBot", "NoteBot", "ReminderBot"]
+__all__ = ["CogTestBot", "EchoBot", "NoteBot", "ReminderBot", "RRCBot"]
diff --git a/vendor/lxmfy/lxmfy/templates/rrc_bot.py b/vendor/lxmfy/lxmfy/templates/rrc_bot.py
new file mode 100644
index 00000000..8dd6ef41
--- /dev/null
+++ b/vendor/lxmfy/lxmfy/templates/rrc_bot.py
@@ -0,0 +1,79 @@
+"""RRC room bot template that joins hubs and echoes mentions."""
+
+from __future__ import annotations
+
+from lxmfy import LXMFBot, RRCMessage
+
+DEFAULT_RRC_HUB = "664fc0e8d2e448658e37bb3f34e6c88f"
+DEFAULT_RRC_ROOMS = ["general"]
+
+
+class RRCBot:
+ """Bot that participates in Reticulum Relay Chat rooms."""
+
+ def __init__(
+ self,
+ hubs: list[str] | None = None,
+ rooms: list[str] | None = None,
+ nick: str | None = None,
+ test_mode: bool = False,
+ reticulum_config_dir: str | None = None,
+ ):
+ """Initialize an RRC-capable bot.
+
+ Args:
+ hubs: Hub destination hashes (hex) to join on startup.
+ rooms: Rooms to auto-join after WELCOME.
+ nick: Nickname advertised on HELLO and room messages.
+ test_mode: Skip RNS initialization when True.
+ reticulum_config_dir: Optional Reticulum config directory override.
+
+ """
+ resolved_hubs = list(hubs) if hubs is not None else [DEFAULT_RRC_HUB]
+ resolved_rooms = list(rooms) if rooms is not None else list(DEFAULT_RRC_ROOMS)
+ self.bot = LXMFBot(
+ name=nick or "RRC Bot",
+ announce=600,
+ announce_enabled=True,
+ first_message_enabled=True,
+ test_mode=test_mode,
+ reticulum_config_dir=reticulum_config_dir,
+ rrc_enabled=bool(resolved_hubs) and not test_mode,
+ rrc_hubs=resolved_hubs,
+ rrc_rooms=resolved_rooms,
+ rrc_nick=nick or "RRCBot",
+ rrc_auto_reconnect=True,
+ )
+ self.setup_handlers()
+
+ def setup_handlers(self) -> None:
+ """Register LXMF and RRC handlers."""
+
+ @self.bot.on_first_message()
+ def welcome(sender, message):
+ self.bot.send(
+ sender,
+ "RRC bot online. I join configured hubs and reply to @mentions in rooms.",
+ )
+ return True
+
+ @self.bot.on_rrc
+ def on_rrc(event, client, payload):
+ if event == "welcome":
+ self.bot.logger.info(
+ "RRC welcomed by %s",
+ payload.get("hub_name") if isinstance(payload, dict) else "hub",
+ )
+ return
+ if event != "msg" or not isinstance(payload, RRCMessage):
+ return
+ if payload.mention and payload.room:
+ reply = f"{payload.nick or 'someone'} mentioned me: {payload.text}"
+ try:
+ client.send_message(payload.room, reply)
+ except Exception as exc:
+ self.bot.logger.error("RRC reply failed: %s", exc)
+
+ def run(self) -> None:
+ """Run the bot event loop."""
+ self.bot.run()
diff --git a/vendor/lxmfy/poetry.lock b/vendor/lxmfy/poetry.lock
index 3e5d701e..aec2f087 100644
--- a/vendor/lxmfy/poetry.lock
+++ b/vendor/lxmfy/poetry.lock
@@ -17,6 +17,65 @@ files = [
docs = ["furo", "jaraco.packaging (>=9.3)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["jaraco.test", "pytest (!=8.0.*)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)"]
+[[package]]
+name = "cbor2"
+version = "6.1.3"
+description = "CBOR (de)serializer with extensive tag support"
+optional = false
+python-versions = ">=3.10"
+groups = ["main"]
+files = [
+ {file = "cbor2-6.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20205c698f9ea4918d1714c459f58f46464c9f0d1e467679da8bca9e4eb17d1c"},
+ {file = "cbor2-6.1.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:4e2e1c8ba6651e12fcf35188f24cbd2344ce0acb087cb7739e5bfb8cec6e12ea"},
+ {file = "cbor2-6.1.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a7bcc957049da506d6e59b4edb5594c68d81a1fa43b56f826839cb15e9a82572"},
+ {file = "cbor2-6.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d9971a2052802422efbbe11fc918c0f784de7280d2e0f45a6e9a8a0dc44e8f53"},
+ {file = "cbor2-6.1.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:165ce348632a4b502d0eb5ff187138d3b34986224273faf2233f842a9331c3ff"},
+ {file = "cbor2-6.1.3-cp310-cp310-win32.whl", hash = "sha256:9084077c4cd7e905ebe47677934c9c6580942c9d1294f99524c0369b10829e78"},
+ {file = "cbor2-6.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:72b23df796d083ff53d561fd86e397aa5c19010192e85ea3838d8ef21c09db6d"},
+ {file = "cbor2-6.1.3-cp310-cp310-win_arm64.whl", hash = "sha256:fcec149218bacf1f98caf44225b67b4908e54e0440ca446ea488aac1ddc85a3b"},
+ {file = "cbor2-6.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84edab2df31c981d258d652a82a3e30eb7368d86d9d7284216282f65403a1e00"},
+ {file = "cbor2-6.1.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:932e0894476fad36b186c0da6e9b1433358bea564a60ae4799e51182568ff29f"},
+ {file = "cbor2-6.1.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5f14159423a984c387982901f67313d6582251b6733c23e8bd925d73173691bf"},
+ {file = "cbor2-6.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:622ec874664b4db54bc6df40f82832ad30fa5c875ad85cde84392ac62bb33d15"},
+ {file = "cbor2-6.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c9144756fa7c9298d5882d1f7ad379c4a0059803a4c70329965a000bc79bf02d"},
+ {file = "cbor2-6.1.3-cp311-cp311-win32.whl", hash = "sha256:144f8cfd2e9149389c34026243aeb646184cd78a2c657822be9bc9e7a2c5f3f5"},
+ {file = "cbor2-6.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:187fd06befc59e6cafafc2709e5f1f3df8afe8bca5646f9cb5b70fc7e6ab1783"},
+ {file = "cbor2-6.1.3-cp311-cp311-win_arm64.whl", hash = "sha256:43f0f694f47958de50fc84e6268a3015cc2a7fce88b231456c053bc5a1c6c828"},
+ {file = "cbor2-6.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d939f55097c21e032f5a2d67592fcc57298986281f219356e2f519e4466f4ea"},
+ {file = "cbor2-6.1.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b025009478d644dab407164fd60e3ef4381af284f5af6966df94c663756d949e"},
+ {file = "cbor2-6.1.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2226d32e102e375737656ad5d141ad8c6ae3e705e04e263f24756f0eb379c6c1"},
+ {file = "cbor2-6.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e61d465244d66ffed36492eef3b44d43795d76a2bba0663a2f15c186af7f7513"},
+ {file = "cbor2-6.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87fe7be8fab6ec4796aa127c1a52e09e79dbafd2aa31caf809cf04b8080a5975"},
+ {file = "cbor2-6.1.3-cp312-cp312-win32.whl", hash = "sha256:da25d345f01e6a40b2e5c57ef96b4dcff7be69394fb62f0f70e07f437f2376a9"},
+ {file = "cbor2-6.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:d5514f693db6fa6f433b4096e9b604e6a7bf151c9ef1d2db86d0858e4c5e768f"},
+ {file = "cbor2-6.1.3-cp312-cp312-win_arm64.whl", hash = "sha256:3d43183d7beb3d3cd198d69b31bd2ee487ed704a1150c75cb0a66d6ad63d8c1a"},
+ {file = "cbor2-6.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:21c74b8ab67977c8b87b727247eeb730145b0068ad6d47f71e9f80f6b48c65f8"},
+ {file = "cbor2-6.1.3-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f291a0ae4c1ed96eadb0afa9752568c7424f7d6fa818676d5e33005fcd22ddd9"},
+ {file = "cbor2-6.1.3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:dc8e44c7bf172687195dcd428157885bc00ea06efc0ea30fb371163b92bef733"},
+ {file = "cbor2-6.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8ccf4d263983d830dd429d2b01f27be58ac02ba7c790c45d861f767eb63963e5"},
+ {file = "cbor2-6.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8719a7a2a2a82168844533389957b8f617a139f5f40e4d0ad7ed905fd3abebd1"},
+ {file = "cbor2-6.1.3-cp313-cp313-win32.whl", hash = "sha256:c73b54ce09dd8d522f3c1540426e36172ba0f34abf3d89eb93909a5e14590003"},
+ {file = "cbor2-6.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:b77df56c462c10eb3444db8ef78d8c3e71d9ef8d021ea92e97c1f9e3aa918690"},
+ {file = "cbor2-6.1.3-cp313-cp313-win_arm64.whl", hash = "sha256:b144be2ab3e9584ee7b6359d2a92fee0a5bec1d00dbd34c215ccc2040ac0b2ab"},
+ {file = "cbor2-6.1.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3f3167ca9920b90db4ff72652db109dfd93b56ee0d583aca12f3a5d9a7019477"},
+ {file = "cbor2-6.1.3-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:48c677971e4b71e685491e1a267d9924dc205e7ddbe3f34fd2562f16c0f6bfca"},
+ {file = "cbor2-6.1.3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ad4f3c6dfc6b83331eb04c6975efb2839ab65a3aa81502bc2b3f7945d4c4aa44"},
+ {file = "cbor2-6.1.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bcd609eab4a39745123bfb28e73311abba3d14975a87f5906e0e8b910d918ac"},
+ {file = "cbor2-6.1.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:672727ecb27d7fb3ca0bf8a58fc489d5374ab1fee680ed3d0348a11d9d3ca78f"},
+ {file = "cbor2-6.1.3-cp314-cp314-win32.whl", hash = "sha256:1413cef2aa7f478a38298cd3492a055e8e8e45d17fb53bbe103e79ca15c33f3c"},
+ {file = "cbor2-6.1.3-cp314-cp314-win_amd64.whl", hash = "sha256:59df264d4a508ba61daaa0bf3c2f92d63275509549a0875c1fa38176f651e4f8"},
+ {file = "cbor2-6.1.3-cp314-cp314-win_arm64.whl", hash = "sha256:b62b5d80a0eb4305cd5f0217faa4d7747bd64fe0dff9b88415e7be3782f8249b"},
+ {file = "cbor2-6.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:856ab525bfc599588b8d2a45babb7c3400c693ea0bb574d818467d997102fe24"},
+ {file = "cbor2-6.1.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a076f35abf1dd0a4de6e2f7f4d4932abafc951a26275b6aa4a3b370c2fd3bbf4"},
+ {file = "cbor2-6.1.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:37ccab1d0bd3f57ff536a41e44165d0c99cb166ac6e5ffb8b93c42304b56e48d"},
+ {file = "cbor2-6.1.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76d257ce797e651fa430b0269e8e8c43549c54ce8b0d12860569b3709bf1326f"},
+ {file = "cbor2-6.1.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4af35baadb66f7c9cbb3998eab469767c04641552416c1c69dd4d3d183797119"},
+ {file = "cbor2-6.1.3-cp314-cp314t-win32.whl", hash = "sha256:61c92661665bccfed4ffa69d1fe10097f2c820d262f56a8cf909a5ebf9f6d8c6"},
+ {file = "cbor2-6.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:dc6bcd030bf5043662b84b0ca0f0ca942491bf509105db30cedca6e2ce82d158"},
+ {file = "cbor2-6.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:da98a5e0ae9487bed497ac74e2c850b49975a3b7b5314b76c3843e2c83a6c8c4"},
+ {file = "cbor2-6.1.3.tar.gz", hash = "sha256:8d70680acb55c04ea5b5ad86da094f9612b53d5a8a65d0f5b3aafc3ce917ecbb"},
+]
+
[[package]]
name = "certifi"
version = "2026.5.20"
@@ -1105,14 +1164,14 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "rns"
-version = "1.3.7"
+version = "1.3.8"
description = "Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
- {file = "rns-1.3.7-py3-none-any.whl", hash = "sha256:95d3bab98513e7f4318ea2dc0b743a21ce9e3c94caee2eb587203ca4b61fd9ed"},
- {file = "rns-1.3.7.tar.gz", hash = "sha256:6757db65c0af212b386b7e44b95eda4e36d6b11cae834258bc6d2d11dac6e125"},
+ {file = "rns-1.3.8-py3-none-any.whl", hash = "sha256:c7f60abc7a2e42869df5be22335cf48a467c988ad67320e55301a183542e4a78"},
+ {file = "rns-1.3.8.tar.gz", hash = "sha256:d5c1f39493aa9b75ab67b83997d4ad5bd357e67e9d629ffa294e31a2ffde347d"},
]
[package.dependencies]
@@ -1256,4 +1315,4 @@ type = ["pytest-mypy (>=1.0.1) ; platform_python_implementation != \"PyPy\""]
[metadata]
lock-version = "2.1"
python-versions = ">=3.11"
-content-hash = "54c949917349ad5896898e82965b442240f489adc3dfce0f36be022979f4ad08"
+content-hash = "701cabb3131f8438f4badabe8f2bf9dc97cc748af3011fda3aeda637bc572706"
diff --git a/vendor/lxmfy/pyproject.toml b/vendor/lxmfy/pyproject.toml
index 4450ed7a..3e4c49f5 100644
--- a/vendor/lxmfy/pyproject.toml
+++ b/vendor/lxmfy/pyproject.toml
@@ -1,12 +1,12 @@
[project]
name = "lxmfy"
-version = "1.6.5"
+version = "2.0.1"
description = "LXMF bot framework for creating bots for the Reticulum Network"
authors = [{name = "Quad4", email = "team@quad4.io"}]
readme = "README.md"
license = "BSD-0-Clause"
requires-python = ">=3.11"
-keywords = ["lxmf", "reticulum", "bot", "framework", "rns"]
+keywords = ["lxmf", "reticulum", "bot", "framework", "rns", "rrc", "cbor"]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
@@ -15,8 +15,9 @@ classifiers = [
"Operating System :: OS Independent",
]
dependencies = [
- "lxmf>=1.1.0",
- "rns>=1.4.0"
+ "lxmf>=1.0.1",
+ "rns>=1.3.9",
+ "cbor2>=5.4.0",
]
[project.urls]
diff --git a/vendor/lxmfy/tests/pytest.ini b/vendor/lxmfy/tests/pytest.ini
index 6aab9cdd..153a5f2e 100644
--- a/vendor/lxmfy/tests/pytest.ini
+++ b/vendor/lxmfy/tests/pytest.ini
@@ -1,4 +1,4 @@
-[tool:pytest]
+[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
@@ -6,10 +6,6 @@ python_functions = test_*
addopts =
--verbose
--tb=short
- --cov=lxmfy
- --cov-report=term-missing
- --cov-report=html:htmlcov
- --cov-fail-under=80
markers =
slow: marks tests as slow (deselect with '-m "not slow"')
integration: marks tests as integration tests
diff --git a/vendor/lxmfy/tests/test_cli.py b/vendor/lxmfy/tests/test_cli.py
index 68af4115..57fcf369 100644
--- a/vendor/lxmfy/tests/test_cli.py
+++ b/vendor/lxmfy/tests/test_cli.py
@@ -155,7 +155,7 @@ class TestInputFunctions:
@patch("lxmfy.cli.print_error")
def test_get_template_choice_invalid_then_valid(self, mock_print_error, mock_input):
"""Test get_template_choice with invalid then valid input."""
- mock_input.side_effect = ["6", "3"] # Invalid then reminder
+ mock_input.side_effect = ["7", "3"] # Invalid then reminder
result = get_template_choice()
assert result == "reminder"
mock_print_error.assert_called_once()
diff --git a/vendor/lxmfy/tests/test_integration.py b/vendor/lxmfy/tests/test_integration.py
index c93d9cf6..41bc6567 100644
--- a/vendor/lxmfy/tests/test_integration.py
+++ b/vendor/lxmfy/tests/test_integration.py
@@ -15,9 +15,9 @@ class TestClientBotCommunication:
original_queue_put = test_bot.queue.put
queued_messages = []
- def capture_queue_put(message):
+ def capture_queue_put(message, *args, **kwargs):
queued_messages.append(message)
- return original_queue_put(message)
+ return original_queue_put(message, *args, **kwargs)
test_bot.queue.put = capture_queue_put
diff --git a/vendor/lxmfy/tests/test_live_lxmf.py b/vendor/lxmfy/tests/test_live_lxmf.py
new file mode 100644
index 00000000..8fffb0a2
--- /dev/null
+++ b/vendor/lxmfy/tests/test_live_lxmf.py
@@ -0,0 +1,416 @@
+"""Live LXMF roundtrip over random online public TCP/backbone nodes.
+
+Fetches online nodes from https://directory.rns.recipes, picks reachable
+TCP/backbone entrypoints at random, connects two LXMFy bots through them,
+and requires a ping/pong roundtrip.
+
+Requires network access and LXMFY_LIVE_LXMF=1. Skips otherwise.
+"""
+
+from __future__ import annotations
+
+import json
+import multiprocessing
+import os
+import random
+import socket
+import sys
+import time
+import urllib.error
+import urllib.request
+from pathlib import Path
+
+import pytest
+
+DIRECTORY_URL = (
+ "https://directory.rns.recipes/api/directory/submitted?search=&type=&status=online"
+)
+LIVE_ENABLED = os.environ.get("LXMFY_LIVE_LXMF", "").strip().lower() in {
+ "1",
+ "true",
+ "yes",
+}
+NODE_TYPES = {"tcp", "backbone"}
+CONNECT_TRIES = int(os.environ.get("LXMFY_LIVE_LXMF_TRIES", "4"))
+PATH_TIMEOUT_S = int(os.environ.get("LXMFY_LIVE_LXMF_PATH_TIMEOUT", "60"))
+ROUNDTRIP_TIMEOUT_S = int(os.environ.get("LXMFY_LIVE_LXMF_ROUNDTRIP_TIMEOUT", "90"))
+
+
+def _log(msg: str) -> None:
+ print(msg, flush=True)
+
+
+def _fetch_online_nodes(url: str = DIRECTORY_URL) -> list[dict]:
+ with urllib.request.urlopen(url, timeout=30) as resp:
+ payload = json.loads(resp.read().decode("utf-8"))
+ rows = payload.get("data", payload) if isinstance(payload, dict) else payload
+ if not isinstance(rows, list):
+ return []
+ nodes = []
+ for row in rows:
+ if not isinstance(row, dict):
+ continue
+ if str(row.get("status", "")).lower() != "online":
+ continue
+ if str(row.get("type", "")).lower() not in NODE_TYPES:
+ continue
+ if not row.get("host") or not row.get("port"):
+ continue
+ # Prefer clearnet TCP host:port targets for live CI hosts.
+ host = str(row["host"])
+ if ":" in host and not host.replace(":", "").isdigit():
+ # Skip Yggdrasil / IPv6-literal-only style hosts unless bracketed.
+ if host.count(":") > 1 and not host.startswith("["):
+ continue
+ nodes.append(row)
+ return nodes
+
+
+def _tcp_reachable(host: str, port: int, timeout: float = 3.0) -> bool:
+ try:
+ with socket.create_connection((host, int(port)), timeout=timeout):
+ return True
+ except OSError:
+ return False
+
+
+def _pick_reachable_nodes(nodes: list[dict], limit: int = CONNECT_TRIES) -> list[dict]:
+ tcp_first = [n for n in nodes if str(n.get("type", "")).lower() == "tcp"]
+ backbone = [n for n in nodes if str(n.get("type", "")).lower() == "backbone"]
+ random.shuffle(tcp_first)
+ random.shuffle(backbone)
+ chosen: list[dict] = []
+ for node in tcp_first + backbone:
+ host = str(node["host"])
+ port = int(node["port"])
+ ok = _tcp_reachable(host, port)
+ _log(f"probe {node.get('name')} {host}:{port} -> {ok}")
+ if ok:
+ chosen.append(node)
+ if len(chosen) >= limit:
+ break
+ return chosen
+
+
+def _interface_block(node: dict) -> str:
+ raw = node.get("config")
+ if isinstance(raw, str) and "type =" in raw:
+ return raw.strip() + "\n"
+ name = str(node.get("name") or "LiveNode").replace("]", "")
+ host = node["host"]
+ port = int(node["port"])
+ ntype = str(node.get("type", "tcp")).lower()
+ if ntype == "backbone":
+ return (
+ f"[[{name}]]\n"
+ f" type = BackboneInterface\n"
+ f" enabled = Yes\n"
+ f" remote = {host}\n"
+ f" target_port = {port}\n"
+ )
+ return (
+ f"[[{name}]]\n"
+ f" type = TCPClientInterface\n"
+ f" enabled = Yes\n"
+ f" target_host = {host}\n"
+ f" target_port = {port}\n"
+ )
+
+
+def _write_rns_config(path: Path, node: dict) -> None:
+ path.mkdir(parents=True, exist_ok=True)
+ (path / "config").write_text(
+ "[reticulum]\n"
+ "enable_transport = No\n"
+ "share_instance = No\n"
+ "\n"
+ "[logging]\n"
+ "loglevel = 3\n"
+ "\n"
+ "[interfaces]\n"
+ f"{_interface_block(node)}",
+ encoding="utf-8",
+ )
+
+
+def _drain_outbound(bot) -> None:
+ while not bot.queue.empty():
+ try:
+ lxm = bot.queue.get(block=False)
+ except Exception:
+ break
+ try:
+ if bot.router:
+ bot.router.handle_outbound(lxm)
+ except Exception as e:
+ _log(f"outbound error: {e}")
+
+
+def _bot_worker(
+ role: str,
+ config_dir: str,
+ peer_hash_hex: str | None,
+ peer_pub_hex: str | None,
+ ready_q: multiprocessing.Queue,
+ result_q: multiprocessing.Queue,
+ stop_event: multiprocessing.Event,
+) -> None:
+ import RNS
+ from LXMF import LXMessage
+
+ from lxmfy import BotConfig, LXMFBot
+
+ os.environ.pop("LXMFY_RETICULUM_CONFIG_DIR", None)
+ try:
+ _log(f"{role}: starting in {config_dir}")
+ bot = LXMFBot(
+ **BotConfig(
+ name=f"Live{role}",
+ config_path=config_dir,
+ reticulum_config_dir=config_dir,
+ storage_path=str(Path(config_dir) / "storage"),
+ announce_enabled=True,
+ announce_immediately=True,
+ first_message_enabled=False,
+ landlock_enabled=False,
+ cogs_enabled=False,
+ message_persistence_enabled=False,
+ propagation_fallback_enabled=False,
+ test_mode=False,
+ ).__dict__,
+ )
+
+ received: list[str] = []
+ delivery_events: list[str] = []
+
+ @bot.on_message()
+ def on_msg(sender, message):
+ raw = message.content
+ content = raw.decode("utf-8") if isinstance(raw, bytes) else (raw or "")
+ received.append(content)
+ _log(f"{role}: recv {content!r} from {sender}")
+ if role == "pong" and content:
+ bot.send(
+ sender,
+ f"pong:{content}",
+ title="",
+ method=LXMessage.OPPORTUNISTIC,
+ )
+ _drain_outbound(bot)
+ return True
+
+ assert bot.local is not None
+ local_hash = RNS.hexrep(bot.local.hash, delimit=False)
+ local_pub = bot.identity.get_public_key().hex()
+ ready_q.put((role, local_hash, local_pub))
+ bot.announce_now(force=True)
+ _drain_outbound(bot)
+
+ if peer_hash_hex and peer_pub_hex:
+ peer = bytes.fromhex(peer_hash_hex)
+ RNS.Identity.remember(
+ RNS.Identity.full_hash(peer),
+ peer,
+ bytes.fromhex(peer_pub_hex),
+ )
+
+ if role == "ping":
+ peer = bytes.fromhex(peer_hash_hex or "")
+ deadline = time.time() + PATH_TIMEOUT_S
+ while time.time() < deadline and not stop_event.is_set():
+ if RNS.Transport.has_path(peer) and RNS.Identity.recall(peer):
+ break
+ RNS.Transport.request_path(peer)
+ if int(time.time()) % 5 == 0:
+ bot.announce_now(force=True)
+ _drain_outbound(bot)
+ time.sleep(0.5)
+ if not (RNS.Transport.has_path(peer) and RNS.Identity.recall(peer)):
+ result_q.put(
+ (
+ role,
+ "no_identity_or_path",
+ {
+ "has_path": RNS.Transport.has_path(peer),
+ "identity": RNS.Identity.recall(peer) is not None,
+ },
+ ),
+ )
+ return
+
+ token = f"live-lxmf-{os.getpid()}-{int(time.time())}"
+ _log(f"{role}: path+identity ok, sending {token!r}")
+
+ # Patch delivery callbacks onto the next queued LXMessage via send path.
+ original_enqueue = bot._enqueue_outbound
+
+ def enqueue_with_hooks(lxm):
+ def ok(_m):
+ delivery_events.append("ok")
+
+ def bad(_m):
+ delivery_events.append("fail")
+
+ try:
+ lxm.register_delivery_callback(ok)
+ lxm.register_failed_callback(bad)
+ except Exception:
+ pass
+ return original_enqueue(lxm)
+
+ bot._enqueue_outbound = enqueue_with_hooks
+ if not bot.send(
+ peer_hash_hex,
+ token,
+ title="",
+ method=LXMessage.OPPORTUNISTIC,
+ ):
+ result_q.put((role, "send_failed", token))
+ return
+ # Keep pumping while LXMF retries opportunistic delivery.
+ _drain_outbound(bot)
+
+ wait_deadline = time.time() + ROUNDTRIP_TIMEOUT_S
+ while time.time() < wait_deadline and not stop_event.is_set():
+ _drain_outbound(bot)
+ if any(m == f"pong:{token}" for m in received):
+ result_q.put((role, "ok", token))
+ return
+ time.sleep(0.2)
+ result_q.put(
+ (
+ role,
+ "timeout",
+ {"received": received[-5:], "delivery": delivery_events},
+ ),
+ )
+ else:
+ while not stop_event.is_set():
+ if int(time.time()) % 8 == 0:
+ bot.announce_now(force=True)
+ _drain_outbound(bot)
+ time.sleep(0.5)
+ result_q.put((role, "stopped", received[-5:]))
+ except Exception as e:
+ _log(f"{role}: error {e}")
+ result_q.put((role, "error", str(e)))
+ finally:
+ try:
+ import RNS
+
+ RNS.Reticulum.exit_handler()
+ except Exception:
+ pass
+ os._exit(0)
+
+
+@pytest.mark.integration
+@pytest.mark.e2e
+@pytest.mark.slow
+@pytest.mark.skipif(
+ not LIVE_ENABLED,
+ reason="Set LXMFY_LIVE_LXMF=1 to run live LXMF mesh roundtrip",
+)
+def test_live_lxmf_ping_pong_random_directory_node(tmp_path):
+ """Ping/pong two LXMFy bots through a random online public TCP/backbone node."""
+ try:
+ nodes = _fetch_online_nodes()
+ except (urllib.error.URLError, TimeoutError, json.JSONDecodeError) as e:
+ pytest.skip(f"directory.rns.recipes unavailable: {e}")
+
+ assert nodes, "no online tcp/backbone nodes in directory"
+ _log(f"directory returned {len(nodes)} online tcp/backbone nodes")
+ candidates = _pick_reachable_nodes(nodes, limit=CONNECT_TRIES)
+ if not candidates:
+ pytest.skip("no reachable online tcp/backbone nodes")
+
+ last_error = None
+ for node in candidates:
+ _log(
+ f"trying node {node.get('name')} "
+ f"({node.get('type')} {node.get('host')}:{node.get('port')})",
+ )
+ ping_dir = tmp_path / f"ping_{node['id']}"
+ pong_dir = tmp_path / f"pong_{node['id']}"
+ _write_rns_config(ping_dir, node)
+ _write_rns_config(pong_dir, node)
+
+ ready_q: multiprocessing.Queue = multiprocessing.Queue()
+ result_q: multiprocessing.Queue = multiprocessing.Queue()
+ stop_event = multiprocessing.Event()
+
+ pong = multiprocessing.Process(
+ target=_bot_worker,
+ args=("pong", str(pong_dir), None, None, ready_q, result_q, stop_event),
+ )
+ pong.start()
+
+ pong_hash = pong_pub = None
+ ready_deadline = time.time() + 60
+ while time.time() < ready_deadline:
+ if not ready_q.empty():
+ role, value, pub = ready_q.get()
+ if role == "pong":
+ pong_hash, pong_pub = value, pub
+ break
+ if not pong.is_alive():
+ break
+ time.sleep(0.2)
+
+ if not pong_hash or not pong_pub:
+ stop_event.set()
+ pong.terminate()
+ pong.join(timeout=5)
+ last_error = f"pong failed on {node.get('name')}"
+ continue
+
+ # Restart pong is already running without peer info; tell ping the peer.
+ # Pong does not need ping identity to receive.
+ ping = multiprocessing.Process(
+ target=_bot_worker,
+ args=(
+ "ping",
+ str(ping_dir),
+ pong_hash,
+ pong_pub,
+ ready_q,
+ result_q,
+ stop_event,
+ ),
+ )
+ ping.start()
+ ping.join(timeout=PATH_TIMEOUT_S + ROUNDTRIP_TIMEOUT_S + 30)
+ stop_event.set()
+ pong.join(timeout=10)
+ for proc in (ping, pong):
+ if proc.is_alive():
+ proc.terminate()
+ proc.join(timeout=5)
+
+ results = []
+ while not result_q.empty():
+ results.append(result_q.get())
+ _log(f"results for {node.get('name')}: {results}")
+
+ if any(r[0] == "ping" and r[1] == "ok" for r in results):
+ _log(
+ f"LIVE_LXMF_PROVED {node.get('name')} "
+ f"{node.get('host')}:{node.get('port')} {results}",
+ )
+ return
+
+ last_error = f"node={node.get('name')} results={results}"
+
+ pytest.fail(
+ f"live LXMF roundtrip failed after trying {len(candidates)} nodes: {last_error}",
+ )
+
+
+if __name__ == "__main__":
+ if not LIVE_ENABLED:
+ print("Set LXMFY_LIVE_LXMF=1", file=sys.stderr)
+ raise SystemExit(2)
+ import tempfile
+
+ with tempfile.TemporaryDirectory() as td:
+ test_live_lxmf_ping_pong_random_directory_node(Path(td))
diff --git a/vendor/lxmfy/tests/test_memory_guards.py b/vendor/lxmfy/tests/test_memory_guards.py
new file mode 100644
index 00000000..052c79f2
--- /dev/null
+++ b/vendor/lxmfy/tests/test_memory_guards.py
@@ -0,0 +1,129 @@
+"""Tests for outbound queue and RRC memory guards."""
+
+from __future__ import annotations
+
+from unittest.mock import MagicMock
+
+from lxmfy import BotConfig, LXMFBot
+from lxmfy.rrc import RRCClient
+from lxmfy.rrc.constants import (
+ MAX_MEMBERS_PER_ROOM,
+ MAX_RESOURCE_EXPECTATIONS,
+ MAX_TRACKED_NICKS,
+)
+from lxmfy.templates.rrc_bot import DEFAULT_RRC_HUB, DEFAULT_RRC_ROOMS, RRCBot
+
+
+def _bot(tmp_path, name: str, **kwargs) -> LXMFBot:
+ config = BotConfig(
+ test_mode=True,
+ config_path=str(tmp_path / f"cfg_{name}"),
+ storage_path=str(tmp_path / f"data_{name}"),
+ announce_enabled=False,
+ cogs_enabled=False,
+ landlock_enabled=False,
+ message_persistence_enabled=True,
+ **kwargs,
+ )
+ return LXMFBot(**config.__dict__)
+
+
+def test_invalid_persisted_destination_is_dropped(tmp_path):
+ bot = _bot(tmp_path, "invalid_dest")
+ bot.storage.set(
+ "persisted_queue",
+ [
+ {
+ "destination": "746573745f73656e646572",
+ "content": "poison",
+ "title": "t",
+ "fields": None,
+ "method": None,
+ },
+ ],
+ )
+ bot._load_persisted_queue()
+ assert bot.storage.get("persisted_queue") == []
+ assert bot.queue.empty()
+
+
+def test_queue_full_drops_oldest(tmp_path):
+ bot = _bot(tmp_path, "full_queue", message_queue_size=2)
+ assert bot.queue.maxsize == 2
+ assert bot.send("aabbccddeeff00112233445566778899", "one")
+ assert bot.send("aabbccddeeff00112233445566778899", "two")
+ assert bot.send("aabbccddeeff00112233445566778899", "three")
+ assert bot.queue.qsize() == 2
+ contents = [
+ item.content.decode("utf-8")
+ if isinstance(item.content, bytes)
+ else item.content
+ for item in list(bot.queue.queue)
+ ]
+ assert contents == ["two", "three"]
+ persisted = bot.storage.get("persisted_queue")
+ assert [item["content"] for item in persisted] == ["two", "three"]
+
+
+def test_persisted_queue_truncated_to_queue_size(tmp_path):
+ bot = _bot(tmp_path, "truncate", message_queue_size=2)
+ dest = "aabbccddeeff00112233445566778899"
+ bot.storage.set(
+ "persisted_queue",
+ [
+ {
+ "destination": dest,
+ "content": f"m{i}",
+ "title": "t",
+ "fields": None,
+ "method": None,
+ }
+ for i in range(5)
+ ],
+ )
+ bot._load_persisted_queue()
+ assert bot.queue.qsize() <= 2
+ assert len(bot.storage.get("persisted_queue")) <= 2
+
+
+def test_rrc_member_and_nick_caps():
+ identity = MagicMock()
+ identity.hash = bytes(range(16))
+ client = RRCClient(hub_hash=bytes(range(16, 32)), identity=identity)
+ room = "general"
+ for i in range(MAX_MEMBERS_PER_ROOM + 50):
+ client._track_member(room, i.to_bytes(16, "big"))
+ assert len(client.members[room]) == MAX_MEMBERS_PER_ROOM
+
+ for i in range(MAX_TRACKED_NICKS + 50):
+ client._track_nick(i.to_bytes(16, "big"), f"n{i}")
+ assert len(client.nicks) == MAX_TRACKED_NICKS
+
+
+def test_rrc_resource_expectation_cap():
+ identity = MagicMock()
+ identity.hash = bytes(range(16))
+ client = RRCClient(hub_hash=bytes(range(16, 32)), identity=identity)
+ for i in range(MAX_RESOURCE_EXPECTATIONS + 10):
+ client._remember_resource_expectation(
+ i.to_bytes(8, "big"),
+ {"kind": "motd", "size": 1, "expires": 0},
+ )
+ assert len(client._resource_expectations) == MAX_RESOURCE_EXPECTATIONS
+
+
+def test_rrc_template_defaults():
+ bot = RRCBot(test_mode=True)
+ assert bot.bot.config.rrc_hubs == [DEFAULT_RRC_HUB]
+ assert bot.bot.config.rrc_rooms == DEFAULT_RRC_ROOMS
+ assert DEFAULT_RRC_HUB == "664fc0e8d2e448658e37bb3f34e6c88f"
+ assert "general" in DEFAULT_RRC_ROOMS
+
+
+def test_rrc_template_uses_home_reticulum(tmp_path, monkeypatch):
+ home_rns = tmp_path / "fake_home_reticulum"
+ home_rns.mkdir()
+ (home_rns / "config").write_text("[reticulum]\nshare_instance = Yes\n")
+ monkeypatch.setenv("LXMFY_RETICULUM_CONFIG_DIR", str(home_rns))
+ bot = RRCBot(test_mode=True)
+ assert bot.bot.reticulum_config_dir == str(home_rns)
diff --git a/vendor/lxmfy/tests/test_message_persistence.py b/vendor/lxmfy/tests/test_message_persistence.py
new file mode 100644
index 00000000..c9b9dd64
--- /dev/null
+++ b/vendor/lxmfy/tests/test_message_persistence.py
@@ -0,0 +1,143 @@
+"""Tests for LXMF outgoing queue crash recovery."""
+
+from __future__ import annotations
+
+from types import SimpleNamespace
+from unittest.mock import MagicMock, patch
+
+from lxmfy import BotConfig, LXMFBot
+
+
+def _bot(tmp_path, name: str) -> LXMFBot:
+ config = BotConfig(
+ test_mode=True,
+ config_path=str(tmp_path / f"cfg_{name}"),
+ storage_path=str(tmp_path / f"data_{name}"),
+ announce_enabled=False,
+ cogs_enabled=False,
+ landlock_enabled=False,
+ message_persistence_enabled=True,
+ )
+ return LXMFBot(**config.__dict__)
+
+
+def test_persisted_queue_survives_and_restores(tmp_path):
+ bot = _bot(tmp_path, "survive")
+ assert bot.send("aabbccddeeff00112233445566778899", "hello crash", title="t")
+ assert bot.storage.get("persisted_queue")
+ assert len(bot.storage.get("persisted_queue")) == 1
+ assert bot.storage.get("persisted_queue")[0]["content"] == "hello crash"
+
+ bot.queue.get(block=False)
+ bot._persist_queue()
+ assert bot.storage.get("persisted_queue") == []
+
+
+def test_persisted_queue_restore_keeps_success_and_failures(tmp_path):
+ bot = _bot(tmp_path, "restore")
+ good = "aabbccddeeff00112233445566778899"
+ fail = "11223344556677889900aabbccddeeff"
+ bot.storage.set(
+ "persisted_queue",
+ [
+ {
+ "destination": good,
+ "content": "one",
+ "title": "t",
+ "fields": None,
+ "method": None,
+ },
+ {
+ "destination": fail,
+ "content": "two",
+ "title": "t",
+ "fields": None,
+ "method": None,
+ },
+ ],
+ )
+
+ original_send = bot.send
+
+ def flaky_send(destination, message, **kwargs):
+ if destination == fail:
+ raise ValueError("boom")
+ return original_send(destination, message, **kwargs)
+
+ bot.send = flaky_send # type: ignore[method-assign]
+ bot._load_persisted_queue()
+ remaining = bot.storage.get("persisted_queue")
+ contents = [item["content"] for item in remaining]
+ assert "one" in contents
+ assert "two" in contents
+ assert not bot.queue.empty()
+
+
+def test_persisted_queue_restore_keeps_unqueued(tmp_path):
+ bot = _bot(tmp_path, "unqueued")
+ bot.storage.set(
+ "persisted_queue",
+ [
+ {
+ "destination": "aabbccddeeff00112233445566778899",
+ "content": "pending",
+ "title": "t",
+ "fields": None,
+ "method": None,
+ },
+ ],
+ )
+
+ bot.send = MagicMock(return_value=False) # type: ignore[method-assign]
+ bot._load_persisted_queue()
+ remaining = bot.storage.get("persisted_queue")
+ assert remaining == [
+ {
+ "destination": "aabbccddeeff00112233445566778899",
+ "content": "pending",
+ "title": "t",
+ "fields": None,
+ "method": None,
+ },
+ ]
+
+
+def test_cleanup_persists_remaining_queue(tmp_path):
+ bot = _bot(tmp_path, "cleanup")
+ bot.send("aabbccddeeff00112233445566778899", "still queued")
+ bot.cleanup()
+ assert bot.storage.get("persisted_queue")
+ assert bot.storage.get("persisted_queue")[0]["content"] == "still queued"
+
+
+def test_persist_serializes_queue_items(tmp_path):
+ bot = _bot(tmp_path, "serialize")
+ msg = SimpleNamespace(
+ destination_hash=bytes.fromhex("aabbccddeeff00112233445566778899"),
+ content=b"bytes content",
+ title=b"title",
+ fields={"a": 1},
+ desired_method="direct",
+ )
+ bot.queue.put(msg)
+ bot._persist_queue()
+ persisted = bot.storage.get("persisted_queue")
+ assert persisted[0]["content"] == "bytes content"
+ assert persisted[0]["title"] == "title"
+ assert persisted[0]["fields"] == {"a": 1}
+
+
+def test_run_requeues_on_outbound_failure(tmp_path):
+ bot = _bot(tmp_path, "requeue")
+ bot.send("aabbccddeeff00112233445566778899", "retry me")
+ bot.router = MagicMock()
+ bot.router.handle_outbound.side_effect = RuntimeError("link down")
+
+ with (
+ patch.object(bot.scheduler, "start"),
+ patch("lxmfy.core.time.sleep", side_effect=KeyboardInterrupt),
+ ):
+ bot.run(delay=0)
+
+ assert not bot.queue.empty()
+ assert bot.storage.get("persisted_queue")[0]["content"] == "retry me"
diff --git a/vendor/lxmfy/tests/test_reticulum_config.py b/vendor/lxmfy/tests/test_reticulum_config.py
new file mode 100644
index 00000000..cdb9579d
--- /dev/null
+++ b/vendor/lxmfy/tests/test_reticulum_config.py
@@ -0,0 +1,250 @@
+"""Tests for Reticulum config discovery and shared-instance isolation."""
+
+from __future__ import annotations
+
+import multiprocessing
+import time
+from pathlib import Path
+
+import pytest
+
+from lxmfy.reticulum_config import (
+ discover_user_reticulum_config_dir,
+ ensure_isolated_share_instance_disabled,
+ is_isolated_reticulum_dir,
+ resolve_reticulum_config_dir,
+)
+
+
+def test_discover_user_reticulum_prefers_system_then_xdg(tmp_path):
+ etc = tmp_path / "etc_reticulum"
+ xdg = tmp_path / ".config" / "reticulum"
+ home = tmp_path / ".reticulum"
+ for path in (etc, xdg, home):
+ path.mkdir(parents=True)
+ (path / "config").write_text("[reticulum]\nshare_instance = Yes\n")
+
+ found = discover_user_reticulum_config_dir(
+ home=str(tmp_path),
+ system_dir=str(etc),
+ )
+ assert found == str(etc.resolve())
+
+ found_xdg = discover_user_reticulum_config_dir(
+ home=str(tmp_path),
+ system_dir=str(tmp_path / "missing_etc"),
+ )
+ assert found_xdg == str(xdg.resolve())
+
+
+def test_discover_user_reticulum_falls_back_to_dot_reticulum(tmp_path):
+ home = tmp_path / ".reticulum"
+ home.mkdir()
+ (home / "config").write_text("[reticulum]\nshare_instance = Yes\n")
+ found = discover_user_reticulum_config_dir(
+ home=str(tmp_path),
+ system_dir=str(tmp_path / "missing_etc"),
+ )
+ assert found == str(home.resolve())
+
+
+def test_resolve_prefers_explicit_then_env_then_discovery(tmp_path, monkeypatch):
+ bot_cfg = tmp_path / "bot"
+ bot_cfg.mkdir()
+ user = tmp_path / ".reticulum"
+ user.mkdir()
+ (user / "config").write_text("[reticulum]\n")
+ explicit = tmp_path / "explicit"
+ explicit.mkdir()
+ missing_etc = str(tmp_path / "missing_etc")
+
+ assert resolve_reticulum_config_dir(
+ str(explicit),
+ str(bot_cfg),
+ environ={},
+ home=str(tmp_path),
+ system_dir=missing_etc,
+ ) == str(explicit.resolve())
+
+ assert resolve_reticulum_config_dir(
+ None,
+ str(bot_cfg),
+ environ={"LXMFY_RETICULUM_CONFIG_DIR": str(user)},
+ home=str(tmp_path),
+ system_dir=missing_etc,
+ ) == str(user.resolve())
+
+ assert resolve_reticulum_config_dir(
+ None,
+ str(bot_cfg),
+ environ={},
+ home=str(tmp_path),
+ system_dir=missing_etc,
+ ) == str(user.resolve())
+
+ empty_home = tmp_path / "empty_home"
+ empty_home.mkdir()
+ assert resolve_reticulum_config_dir(
+ None,
+ str(bot_cfg),
+ environ={},
+ home=str(empty_home),
+ system_dir=missing_etc,
+ ) == str(bot_cfg.resolve())
+
+
+def test_ensure_isolated_creates_share_instance_no(tmp_path):
+ rns_dir = tmp_path / "isolated"
+ assert ensure_isolated_share_instance_disabled(str(rns_dir)) is True
+ text = (rns_dir / "config").read_text()
+ assert "share_instance = No" in text
+ assert ensure_isolated_share_instance_disabled(str(rns_dir)) is False
+
+
+def test_ensure_isolated_rewrites_share_instance_yes(tmp_path):
+ rns_dir = tmp_path / "isolated"
+ rns_dir.mkdir()
+ (rns_dir / "config").write_text(
+ "[reticulum]\nshare_instance = Yes\nenable_transport = Yes\n",
+ )
+ assert ensure_isolated_share_instance_disabled(str(rns_dir)) is True
+ text = (rns_dir / "config").read_text()
+ assert "share_instance = No" in text
+ assert "share_instance = Yes" not in text
+
+
+def test_is_isolated_reticulum_dir(tmp_path):
+ bot = tmp_path / "bot"
+ other = tmp_path / "other"
+ bot.mkdir()
+ other.mkdir()
+ assert is_isolated_reticulum_dir(str(bot), str(bot)) is True
+ assert is_isolated_reticulum_dir(str(other), str(bot)) is False
+
+
+def _shared_instance_worker(
+ role: str,
+ cfg: str,
+ q: multiprocessing.Queue,
+ delay: float = 0.0,
+):
+ import RNS
+
+ if delay:
+ time.sleep(delay)
+ try:
+ r = RNS.Reticulum(configdir=cfg, loglevel=RNS.LOG_ERROR)
+ q.put(
+ (
+ role,
+ "started",
+ r.is_shared_instance,
+ r.is_connected_to_shared_instance,
+ r.is_standalone_instance,
+ ),
+ )
+ try:
+ client = r.get_rpc_client()
+ client.close()
+ q.put((role, "rpc_ok"))
+ except Exception as e:
+ q.put((role, f"{type(e).__name__}: {e}"))
+ if role == "master":
+ time.sleep(6)
+ finally:
+ try:
+ RNS.Reticulum.exit_handler()
+ except Exception:
+ pass
+
+
+def _write_tcp_shared_config(
+ path: Path, *, share: bool, iface: int, control: int
+) -> None:
+ path.mkdir(parents=True, exist_ok=True)
+ share_val = "Yes" if share else "No"
+ (path / "config").write_text(
+ "[reticulum]\n"
+ "enable_transport = Yes\n"
+ f"share_instance = {share_val}\n"
+ "shared_instance_type = tcp\n"
+ f"shared_instance_port = {iface}\n"
+ f"instance_control_port = {control}\n"
+ "\n"
+ "[logging]\n"
+ "loglevel = 3\n"
+ "\n"
+ "[interfaces]\n",
+ encoding="utf-8",
+ )
+
+
+@pytest.mark.integration
+def test_colliding_shared_instance_rejects_digest(tmp_path):
+ """Different config dirs on the same shared ports fail RPC auth."""
+ master = tmp_path / "master"
+ client = tmp_path / "client"
+ _write_tcp_shared_config(master, share=True, iface=47628, control=47629)
+ _write_tcp_shared_config(client, share=True, iface=47628, control=47629)
+
+ q: multiprocessing.Queue = multiprocessing.Queue()
+ p1 = multiprocessing.Process(
+ target=_shared_instance_worker,
+ args=("master", str(master), q),
+ )
+ p2 = multiprocessing.Process(
+ target=_shared_instance_worker,
+ args=("client", str(client), q, 2.0),
+ )
+ p1.start()
+ p2.start()
+ p2.join(timeout=20)
+ p1.terminate()
+ p1.join(timeout=5)
+
+ results = []
+ while not q.empty():
+ results.append(q.get())
+
+ assert any(
+ r[0] == "client" and "digest sent was rejected" in str(r[1]) for r in results
+ )
+
+
+@pytest.mark.integration
+def test_isolated_share_instance_avoids_digest_rejection(tmp_path):
+ """Guarantee: isolated configs with share_instance=No do not RPC-collide.
+
+ RETICULUM_DIGEST_PROVED
+ """
+ master = tmp_path / "master"
+ client = tmp_path / "client"
+ ensure_isolated_share_instance_disabled(str(master))
+ ensure_isolated_share_instance_disabled(str(client))
+ _write_tcp_shared_config(master, share=False, iface=47728, control=47729)
+ _write_tcp_shared_config(client, share=False, iface=47738, control=47739)
+
+ q: multiprocessing.Queue = multiprocessing.Queue()
+ p1 = multiprocessing.Process(
+ target=_shared_instance_worker,
+ args=("master", str(master), q),
+ )
+ p2 = multiprocessing.Process(
+ target=_shared_instance_worker,
+ args=("client", str(client), q, 1.5),
+ )
+ p1.start()
+ p2.start()
+ p2.join(timeout=20)
+ p1.terminate()
+ p1.join(timeout=5)
+
+ results = []
+ while not q.empty():
+ results.append(q.get())
+
+ started = [r for r in results if len(r) >= 5 and r[1] == "started"]
+ assert len(started) == 2
+ assert all(r[4] is True for r in started)
+ assert not any("digest sent was rejected" in str(item) for item in results)
+ print("RETICULUM_DIGEST_PROVED")
diff --git a/vendor/lxmfy/tests/test_rrc.py b/vendor/lxmfy/tests/test_rrc.py
new file mode 100644
index 00000000..9602d991
--- /dev/null
+++ b/vendor/lxmfy/tests/test_rrc.py
@@ -0,0 +1,587 @@
+"""Tests for RRC CBOR encoding and client session behavior."""
+
+from __future__ import annotations
+
+from unittest.mock import MagicMock, patch
+
+import cbor2
+import pytest
+import RNS
+from hypothesis import given, settings
+from hypothesis import strategies as st
+
+from lxmfy import BotConfig, LXMFBot
+from lxmfy.rrc import (
+ DEFAULT_DEST_NAME,
+ RRCClient,
+ RRCManager,
+ RRC_VERSION,
+ STATUS_CONNECTED,
+ T_ACTION,
+ T_ERROR,
+ T_HELLO,
+ T_JOINED,
+ T_MSG,
+ T_NOTICE,
+ T_PING,
+ T_PONG,
+ T_WELCOME,
+ decode_envelope,
+ encode_envelope,
+ make_envelope,
+ normalize_room,
+)
+from lxmfy.rrc.constants import (
+ B_HELLO_CAPS,
+ B_HELLO_NAME,
+ B_HELLO_VER,
+ B_WELCOME_HUB,
+ B_WELCOME_LIMITS,
+ B_WELCOME_VER,
+ CAP_ACTION,
+ K_BODY,
+ K_ID,
+ K_NICK,
+ K_ROOM,
+ K_SRC,
+ K_T,
+ K_TS,
+ K_V,
+ L_MAX_MSG_BODY_BYTES,
+ L_MAX_NICK_BYTES,
+)
+
+
+def _src() -> bytes:
+ return bytes(range(16))
+
+
+def test_normalize_room():
+ assert normalize_room(" Lobby ") == "lobby"
+ assert normalize_room("#General") == "#general"
+ with pytest.raises(ValueError):
+ normalize_room(" ")
+ with pytest.raises(ValueError):
+ normalize_room(None)
+
+
+def test_make_envelope_roundtrip_cbor():
+ src = _src()
+ env = make_envelope(
+ T_MSG,
+ src=src,
+ room="lobby",
+ body="Hello, world!",
+ nick="alice",
+ mid=b"\x7a\x3f\x8e\x12\x45\xc9\xa1\x6d",
+ ts=1737849600000,
+ )
+ raw = encode_envelope(env)
+ decoded = decode_envelope(raw)
+ assert decoded is not None
+ assert decoded[K_V] == RRC_VERSION
+ assert decoded[K_T] == T_MSG
+ assert decoded[K_SRC] == src
+ assert decoded[K_ROOM] == "lobby"
+ assert decoded[K_BODY] == "Hello, world!"
+ assert decoded[K_NICK] == "alice"
+ assert decoded[K_ID] == b"\x7a\x3f\x8e\x12\x45\xc9\xa1\x6d"
+ assert decoded[K_TS] == 1737849600000
+
+
+def test_envelope_rejects_bad_src():
+ with pytest.raises(ValueError):
+ make_envelope(T_HELLO, src=b"short")
+
+
+def test_decode_malformed_returns_none():
+ assert decode_envelope(b"not-cbor") is None
+ assert decode_envelope(cbor2.dumps(["list"])) is None
+
+
+def test_unknown_keys_preserved():
+ src = _src()
+ env = make_envelope(T_MSG, src=src, room="x", body="y")
+ env[99] = "extension"
+ decoded = decode_envelope(encode_envelope(env))
+ assert decoded is not None
+ assert decoded[99] == "extension"
+
+
+def test_spec_size_budget_example():
+ """Worst-case MSG budget from 3-RRC should stay near the MTU."""
+ import os
+
+ src = _src()
+ env = make_envelope(
+ T_MSG,
+ src=src,
+ room="a" * 64,
+ body="b" * 350,
+ nick="c" * 32,
+ mid=os.urandom(8),
+ ts=(1 << 40),
+ )
+ encoded = encode_envelope(env)
+ assert len(encoded) <= 500
+
+
+@given(
+ room=st.text(min_size=1, max_size=32).filter(lambda s: s.strip()),
+ body=st.text(min_size=1, max_size=120),
+ nick=st.one_of(st.none(), st.text(min_size=1, max_size=16)),
+)
+@settings(max_examples=40, deadline=None)
+def test_envelope_roundtrip_property(room, body, nick):
+ src = _src()
+ env = make_envelope(T_MSG, src=src, room=room, body=body, nick=nick)
+ decoded = decode_envelope(encode_envelope(env))
+ assert decoded is not None
+ assert decoded[K_T] == T_MSG
+ assert decoded[K_BODY] == body
+ assert decoded[K_ROOM] == room
+ if nick:
+ assert decoded[K_NICK] == nick
+ else:
+ assert K_NICK not in decoded
+
+
+def _client(events: list | None = None) -> RRCClient:
+ identity = MagicMock()
+ identity.hash = _src()
+ hub = bytes(range(16, 32))
+
+ def on_event(event, client, payload):
+ if events is not None:
+ events.append((event, payload))
+
+ return RRCClient(
+ identity=identity,
+ hub_hash=hub,
+ nick="TestBot",
+ auto_reconnect=False,
+ on_event=on_event,
+ )
+
+
+def test_client_handles_welcome_and_limits():
+ events = []
+ client = _client(events)
+ body = {
+ B_WELCOME_HUB: "ExampleHub",
+ B_WELCOME_VER: "0.1.0",
+ B_WELCOME_LIMITS: {
+ L_MAX_NICK_BYTES: 24,
+ L_MAX_MSG_BODY_BYTES: 200,
+ },
+ }
+ env = make_envelope(T_WELCOME, src=bytes(range(16, 32)), body=body)
+ client._on_packet(encode_envelope(env))
+ assert client.welcomed is True
+ assert client.status == STATUS_CONNECTED
+ assert client.hub_name == "ExampleHub"
+ assert client.max_nick_bytes == 24
+ assert client.max_msg_body_bytes == 200
+ assert any(e[0] == "welcome" for e in events)
+
+
+def test_client_handles_msg_and_mention():
+ events = []
+ client = _client(events)
+ peer = bytes(range(32, 48))
+ env = make_envelope(
+ T_MSG,
+ src=peer,
+ room="Lobby",
+ body="hey @TestBot are you there?",
+ nick="alice",
+ )
+ client._on_packet(encode_envelope(env))
+ msgs = [p for e, p in events if e == "msg"]
+ assert len(msgs) == 1
+ assert msgs[0].room == "lobby"
+ assert msgs[0].mention is True
+ assert msgs[0].nick == "alice"
+ assert client.nicks[peer] == "alice"
+
+
+def test_client_handles_notice_and_action():
+ events = []
+ client = _client(events)
+ peer = bytes(range(32, 48))
+ notice = make_envelope(T_NOTICE, src=peer, room="lobby", body="system note")
+ action = make_envelope(T_ACTION, src=peer, room="lobby", body="waves")
+ client._on_packet(encode_envelope(notice))
+ client._on_packet(encode_envelope(action))
+ kinds = [e for e, _ in events]
+ assert "notice" in kinds
+ assert "action" in kinds
+
+
+def test_client_ping_pong():
+ events = []
+ client = _client(events)
+ client.link = MagicMock()
+ client.link.status = RNS.Link.ACTIVE
+
+ with patch.object(client, "_send_env") as send_env:
+ ping = make_envelope(T_PING, src=bytes(range(16, 32)), body=b"12345678")
+ client._on_packet(encode_envelope(ping))
+ assert send_env.called
+ pong_env = send_env.call_args[0][0]
+ assert pong_env[K_T] == T_PONG
+ assert pong_env[K_BODY] == b"12345678"
+
+ body = b"abcdefgh"
+ client._pending_pings[body] = 1
+ pong = make_envelope(T_PONG, src=bytes(range(16, 32)), body=body)
+ client._on_packet(encode_envelope(pong))
+ assert any(e[0] == "pong" for e in events)
+
+
+def test_client_joined_and_error():
+ events = []
+ client = _client(events)
+ client._pending_joins.add("lobby")
+ joined = make_envelope(
+ T_JOINED,
+ src=bytes(range(16, 32)),
+ room="lobby",
+ body=[_src()],
+ )
+ client._on_packet(encode_envelope(joined))
+ assert "lobby" in client.rooms
+ assert any(e[0] == "joined" for e in events)
+
+ client._pending_joins.add("secret")
+ err = make_envelope(
+ T_ERROR,
+ src=bytes(range(16, 32)),
+ room="secret",
+ body="denied",
+ )
+ client._on_packet(encode_envelope(err))
+ assert "secret" not in client.rooms
+ assert any(e[0] == "error" for e in events)
+
+
+def test_client_send_message_requires_active_link():
+ client = _client()
+ with pytest.raises(RuntimeError):
+ client.send_message("lobby", "hi")
+
+
+def test_client_send_message_encodes_and_tracks_id():
+ client = _client()
+ client.link = MagicMock()
+ client.link.status = RNS.Link.ACTIVE
+ client.welcomed = True
+ with (
+ patch.object(client, "_packet_would_fit", return_value=True),
+ patch("lxmfy.rrc.client.RNS.Packet") as packet_cls,
+ ):
+ packet = MagicMock()
+ packet_cls.return_value = packet
+ mid = client.send_message("Lobby", "hello there")
+ assert isinstance(mid, bytes)
+ assert mid in client._sent_ids
+ packet.send.assert_called_once()
+ payload = packet_cls.call_args[0][1]
+ decoded = decode_envelope(payload)
+ assert decoded is not None
+ assert decoded[K_T] == T_MSG
+ assert decoded[K_ROOM] == "lobby"
+ assert decoded[K_BODY] == "hello there"
+
+
+def test_hello_body_shape():
+ client = _client()
+ link = MagicMock()
+ with patch("lxmfy.rrc.client.RNS.Packet") as packet_cls:
+ packet = MagicMock()
+ packet_cls.return_value = packet
+ client._send_hello(link)
+ payload = packet_cls.call_args[0][1]
+ env = decode_envelope(payload)
+ assert env is not None
+ assert env[K_T] == T_HELLO
+ assert env[K_BODY][B_HELLO_NAME] == "lxmfy"
+ assert env[K_BODY][B_HELLO_VER]
+ assert env[K_BODY][B_HELLO_CAPS][CAP_ACTION] is True
+
+
+def test_manager_connect_and_status():
+ identity = MagicMock()
+ identity.hash = _src()
+ manager = RRCManager(identity=identity, nick="MgrBot", auto_reconnect=False)
+ hub = bytes(range(16, 32)).hex()
+
+ with patch.object(RRCClient, "connect") as connect:
+ client = manager.connect(hub, rooms=["lobby"])
+ connect.assert_called_once()
+ assert client.nick == "MgrBot"
+ assert "lobby" in client._auto_join_rooms
+ assert manager.get(hub) is client
+ assert len(manager.status()) == 1
+
+ manager.disconnect(hub)
+ assert manager.get(hub) is None
+
+
+def test_bot_exposes_rrc_in_test_mode(tmp_path):
+ config = BotConfig(
+ test_mode=True,
+ config_path=str(tmp_path / "cfg"),
+ storage_path=str(tmp_path / "data"),
+ announce_enabled=False,
+ cogs_enabled=False,
+ landlock_enabled=False,
+ rrc_enabled=True,
+ rrc_hubs=["aabbccddeeff00112233445566778899"],
+ rrc_rooms=["lobby"],
+ rrc_nick="UnitBot",
+ )
+ bot = LXMFBot(**config.__dict__)
+ assert isinstance(bot.rrc, RRCManager)
+ assert bot.rrc.nick == "UnitBot"
+ assert bot.rrc.dest_name == DEFAULT_DEST_NAME
+
+ called = []
+
+ @bot.on_rrc
+ def handler(event, client, payload):
+ called.append(event)
+
+ bot._rrc_event("welcome", MagicMock(hub_hash=_src()), {"hub_name": "x"})
+ assert called == ["welcome"]
+
+ with pytest.raises(RuntimeError):
+ bot.connect_rrc("aabbccddeeff00112233445566778899")
+
+
+def test_default_dest_name_constant():
+ assert DEFAULT_DEST_NAME == "rrc.hub"
+ assert RRC_VERSION == 1
+
+
+def test_validate_envelope_rejects_bad_fields():
+ from lxmfy.rrc import validate_envelope
+
+ src = _src()
+ good = make_envelope(T_MSG, src=src, room="lobby", body="hi")
+ assert validate_envelope(good) is True
+
+ bad_version = dict(good)
+ bad_version[K_V] = 99
+ assert validate_envelope(bad_version) is False
+
+ bad_src = dict(good)
+ bad_src[K_SRC] = b"short"
+ assert validate_envelope(bad_src) is False
+
+ bad_id = dict(good)
+ bad_id[K_ID] = b"1234"
+ assert validate_envelope(bad_id) is False
+
+
+def test_pre_welcome_send_rejected():
+ client = _client()
+ client.link = MagicMock()
+ client.link.status = RNS.Link.ACTIVE
+ client.welcomed = False
+ with pytest.raises(RuntimeError, match="not welcomed"):
+ client.send_message("lobby", "too early")
+
+
+def test_on_closed_clears_session_and_preserves_rejoin():
+ client = _client()
+ client.auto_reconnect = False
+ client.rooms.add("lobby")
+ client.nicks[_src()] = "alice"
+ client._pending_pings[b"12345678"] = 1
+ client.welcomed = True
+ client._on_closed(MagicMock())
+ assert client.rooms == set()
+ assert client.nicks == {}
+ assert client._pending_pings == {}
+ assert client.welcomed is False
+ assert "lobby" in client._rejoin_rooms
+
+
+def test_welcome_rejoins_previous_rooms():
+ client = _client()
+ client.link = MagicMock()
+ client.link.status = RNS.Link.ACTIVE
+ client._rejoin_rooms.add("lobby")
+ client._auto_join_rooms = ["ops"]
+ with patch.object(client, "join") as join:
+ env = make_envelope(
+ T_WELCOME,
+ src=bytes(range(16, 32)),
+ body={B_WELCOME_HUB: "Hub"},
+ )
+ client._on_packet(encode_envelope(env))
+ joined = {call.args[0] for call in join.call_args_list}
+ assert joined == {"lobby", "ops"}
+ assert client.connected is True
+
+
+def test_hub_limit_enforcement():
+ client = _client()
+ client.link = MagicMock()
+ client.link.status = RNS.Link.ACTIVE
+ client.welcomed = True
+ client.max_msg_body_bytes = 5
+ with pytest.raises(ValueError, match="too long"):
+ client.send_message("lobby", "toolong")
+
+ client.max_room_name_bytes = 3
+ with pytest.raises(ValueError, match="room name too long"):
+ client.join("lobby")
+
+ client.max_room_name_bytes = 64
+ client.max_rooms_per_session = 1
+ client.rooms.add("a")
+ with pytest.raises(RuntimeError, match="max rooms"):
+ client.join("b")
+
+ with pytest.raises(ValueError, match="nick too long"):
+ client.set_nick("x" * 100)
+
+
+def test_rate_limit_enforcement():
+ client = _client()
+ client.link = MagicMock()
+ client.link.status = RNS.Link.ACTIVE
+ client.welcomed = True
+ client.rate_limit_msgs_per_minute = 2
+ with (
+ patch.object(client, "_packet_would_fit", return_value=True),
+ patch("lxmfy.rrc.client.RNS.Packet") as packet_cls,
+ ):
+ packet_cls.return_value = MagicMock()
+ client.send_message("lobby", "one")
+ client.send_message("lobby", "two")
+ with pytest.raises(RuntimeError, match="rate limit"):
+ client.send_message("lobby", "three")
+
+
+def test_mention_word_boundary():
+ events = []
+ client = _client(events)
+ peer = bytes(range(32, 48))
+ env = make_envelope(
+ T_MSG,
+ src=peer,
+ room="lobby",
+ body="notatestbot but @TestBot yes",
+ nick="alice",
+ )
+ client._on_packet(encode_envelope(env))
+ msgs = [p for e, p in events if e == "msg"]
+ assert msgs and msgs[0].mention is True
+
+ events.clear()
+ env2 = make_envelope(
+ T_MSG,
+ src=peer,
+ room="lobby",
+ body="email testbot@example.com",
+ nick="alice",
+ )
+ client._on_packet(encode_envelope(env2))
+ msgs = [p for e, p in events if e == "msg"]
+ assert msgs and msgs[0].mention is False
+
+
+def test_resource_envelope_expectation():
+ from lxmfy.rrc.constants import (
+ B_RES_ENCODING,
+ B_RES_ID,
+ B_RES_KIND,
+ B_RES_SHA256,
+ B_RES_SIZE,
+ T_RESOURCE_ENVELOPE,
+ )
+
+ client = _client()
+ rid = b"12345678"
+ body = {
+ B_RES_ID: rid,
+ B_RES_KIND: "motd",
+ B_RES_SIZE: 5,
+ B_RES_SHA256: __import__("hashlib").sha256(b"hello").digest(),
+ B_RES_ENCODING: "utf-8",
+ }
+ env = make_envelope(T_RESOURCE_ENVELOPE, src=bytes(range(16, 32)), body=body)
+ client._on_packet(encode_envelope(env))
+ assert rid in client._resource_expectations
+ assert client._resource_expectations[rid]["kind"] == "motd"
+
+
+def test_manager_send_action_and_persist(tmp_path):
+ from lxmfy.storage import JSONStorage, Storage
+
+ identity = MagicMock()
+ identity.hash = _src()
+ storage = Storage(JSONStorage(str(tmp_path / "store")))
+ manager = RRCManager(
+ identity=identity,
+ nick="MgrBot",
+ auto_reconnect=False,
+ storage=storage,
+ persist_sessions=True,
+ )
+ hub = bytes(range(16, 32)).hex()
+ with patch.object(RRCClient, "connect"):
+ client = manager.connect(hub, rooms=["lobby"])
+ client.rooms.add("lobby")
+ manager.save_sessions()
+
+ saved = storage.get("rrc_sessions", [])
+ assert saved and saved[0]["hub_hash"] == hub
+ assert "lobby" in saved[0]["rooms"]
+
+ client.link = MagicMock()
+ client.link.status = RNS.Link.ACTIVE
+ client.welcomed = True
+ client.status = STATUS_CONNECTED
+ with (
+ patch.object(client, "_packet_would_fit", return_value=True),
+ patch("lxmfy.rrc.client.RNS.Packet") as packet_cls,
+ ):
+ packet_cls.return_value = MagicMock()
+ mid = manager.send_action("lobby", "waves", hub_hash=hub)
+ assert isinstance(mid, bytes)
+
+ with (
+ patch.object(client, "_packet_would_fit", return_value=True),
+ patch("lxmfy.rrc.client.RNS.Packet") as packet_cls,
+ ):
+ packet_cls.return_value = MagicMock()
+ manager.join("ops", hub_hash=hub)
+
+ saved = storage.get("rrc_sessions", [])
+ assert "ops" in saved[0]["rooms"] or "ops" in client._rejoin_rooms
+
+
+def test_save_sessions_includes_auto_join_rooms(tmp_path):
+ from lxmfy.storage import JSONStorage, Storage
+
+ identity = MagicMock()
+ identity.hash = _src()
+ storage = Storage(JSONStorage(str(tmp_path / "store2")))
+ manager = RRCManager(
+ identity=identity,
+ nick="MgrBot",
+ auto_reconnect=False,
+ storage=storage,
+ persist_sessions=True,
+ )
+ hub = bytes(range(16, 32)).hex()
+ with patch.object(RRCClient, "connect"):
+ client = manager.connect(hub, rooms=["general"])
+ saved = storage.get("rrc_sessions", [])
+ assert saved[0]["rooms"] == ["general"]
+ assert "general" in client._auto_join_rooms
+ assert "general" in client._rejoin_rooms
diff --git a/vendor/lxmfy/tests/test_rrc_live.py b/vendor/lxmfy/tests/test_rrc_live.py
new file mode 100644
index 00000000..bbf118f9
--- /dev/null
+++ b/vendor/lxmfy/tests/test_rrc_live.py
@@ -0,0 +1,238 @@
+"""Live RRC smoke test against a local rrcd hub over TCP loopback.
+
+Requires rrcd installed and LXMFY_LIVE_RRC=1. Skips otherwise.
+"""
+
+from __future__ import annotations
+
+import os
+import socket
+import subprocess
+import tempfile
+import time
+from pathlib import Path
+
+import pytest
+import RNS
+
+from lxmfy.rrc import RRCClient
+
+REPO_ROOT = Path(__file__).resolve().parents[1]
+
+
+def _can_import_rrcd() -> bool:
+ try:
+ import rrcd # noqa: F401
+
+ return True
+ except Exception:
+ return False
+
+
+rrcd_available = _can_import_rrcd()
+live_enabled = os.environ.get("LXMFY_LIVE_RRC", "").strip().lower() in {
+ "1",
+ "true",
+ "yes",
+}
+
+
+def _free_port() -> int:
+ sock = socket.socket()
+ sock.bind(("127.0.0.1", 0))
+ port = sock.getsockname()[1]
+ sock.close()
+ return port
+
+
+def _write_rns_config(path: Path, *, server: bool, port: int) -> None:
+ path.mkdir(parents=True, exist_ok=True)
+ if server:
+ iface = f"""
+[interfaces]
+ [[TCP Server]]
+ type = TCPServerInterface
+ enabled = Yes
+ listen_ip = 127.0.0.1
+ listen_port = {port}
+"""
+ else:
+ iface = f"""
+[interfaces]
+ [[TCP Client]]
+ type = TCPClientInterface
+ enabled = Yes
+ target_host = 127.0.0.1
+ target_port = {port}
+"""
+ (path / "config").write_text(
+ f"""
+[reticulum]
+ enable_transport = Yes
+ share_instance = No
+ panic_on_interface_error = No
+
+[logging]
+ loglevel = 3
+{iface}
+""".lstrip(),
+ )
+
+
+@pytest.mark.integration
+@pytest.mark.e2e
+@pytest.mark.skipif(not rrcd_available, reason="rrcd not installed")
+@pytest.mark.skipif(
+ not live_enabled,
+ reason="Set LXMFY_LIVE_RRC=1 to run live RRC hub smoke test",
+)
+def test_live_rrc_hello_join_msg_roundtrip():
+ """Run rrcd on a TCP server and connect an LXMFy RRC client over TCP."""
+ with tempfile.TemporaryDirectory() as tmp:
+ home = Path(tmp)
+ port = _free_port()
+ hub_rns = home / "hub_rns"
+ client_rns = home / "client_rns"
+ rrcd_home = home / "rrcd"
+ rrcd_home.mkdir()
+ _write_rns_config(hub_rns, server=True, port=port)
+ _write_rns_config(client_rns, server=False, port=port)
+
+ env = os.environ.copy()
+ env["RRCD_HOME"] = str(rrcd_home)
+
+ init = subprocess.run(
+ [
+ "poetry",
+ "run",
+ "python",
+ "-m",
+ "rrcd",
+ "--configdir",
+ str(hub_rns),
+ "--hub-name",
+ "LXMFyLiveHub",
+ ],
+ cwd=str(REPO_ROOT),
+ env=env,
+ capture_output=True,
+ text=True,
+ check=False,
+ )
+ identity_path = rrcd_home / "hub_identity"
+ assert identity_path.is_file(), init.stdout + init.stderr
+
+ # rrcd.toml defaults configdir to empty and would override --configdir.
+ rrcd_toml = rrcd_home / "rrcd.toml"
+ text = rrcd_toml.read_text()
+ text = text.replace('configdir = ""', f'configdir = "{hub_rns}"')
+ if "announce_period_s = 0.0" in text:
+ text = text.replace("announce_period_s = 0.0", "announce_period_s = 3.0")
+ rrcd_toml.write_text(text)
+
+ hub_proc = subprocess.Popen(
+ [
+ "poetry",
+ "run",
+ "python",
+ "-m",
+ "rrcd",
+ "--configdir",
+ str(hub_rns),
+ "--hub-name",
+ "LXMFyLiveHub",
+ "--announce-period",
+ "3",
+ "--log-level",
+ "INFO",
+ ],
+ cwd=str(REPO_ROOT),
+ env=env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ text=True,
+ )
+
+ try:
+ port_deadline = time.time() + 15
+ while time.time() < port_deadline:
+ try:
+ with socket.create_connection(("127.0.0.1", port), timeout=0.5):
+ break
+ except OSError:
+ time.sleep(0.2)
+ else:
+ out = ""
+ try:
+ hub_proc.terminate()
+ out = hub_proc.communicate(timeout=2)[0] or ""
+ except Exception:
+ pass
+ pytest.fail(f"hub TCP server did not start on {port}\n{out}")
+
+ RNS.Reticulum(configdir=str(client_rns), loglevel=RNS.LOG_ERROR)
+ time.sleep(6.0)
+
+ hub_identity = RNS.Identity.from_file(str(identity_path))
+ app_name, aspects = RNS.Destination.app_and_aspects_from_name("rrc.hub")
+ hub_hash = RNS.Destination.hash(hub_identity, app_name, *aspects)
+ RNS.Identity.remember(
+ RNS.Identity.full_hash(hub_hash),
+ hub_hash,
+ hub_identity.get_public_key(),
+ )
+
+ path_deadline = time.time() + 20
+ while time.time() < path_deadline and not RNS.Transport.has_path(hub_hash):
+ RNS.Transport.request_path(hub_hash)
+ time.sleep(0.5)
+
+ events = []
+
+ def on_event(event, client, payload):
+ events.append((event, payload))
+
+ client = RRCClient(
+ identity=RNS.Identity(),
+ hub_hash=hub_hash,
+ nick="LiveBot",
+ auto_reconnect=False,
+ on_event=on_event,
+ )
+ client.set_auto_join(["lobby"])
+ client.connect()
+
+ deadline = time.time() + 30
+ while time.time() < deadline and not client.connected:
+ time.sleep(0.2)
+
+ assert client.connected, (
+ f"did not get WELCOME: {client.status_text} "
+ f"has_path={RNS.Transport.has_path(hub_hash)} events={events}"
+ )
+
+ join_deadline = time.time() + 15
+ while time.time() < join_deadline and "lobby" not in client.rooms:
+ time.sleep(0.2)
+ assert "lobby" in client.rooms
+
+ mid = client.send_message("lobby", "live smoke from lxmfy")
+ assert isinstance(mid, bytes) and len(mid) == 8
+ client.disconnect()
+ assert any(e[0] == "welcome" for e in events)
+ finally:
+ hub_proc.terminate()
+ try:
+ hub_proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ hub_proc.kill()
+ try:
+ out = hub_proc.stdout.read() if hub_proc.stdout else ""
+ if out:
+ print(out[-4000:])
+ except Exception:
+ pass
+ try:
+ RNS.Reticulum.exit_handler()
+ except Exception:
+ pass
diff --git a/vendor/lxmfy/uv.lock b/vendor/lxmfy/uv.lock
new file mode 100644
index 00000000..45870d5e
--- /dev/null
+++ b/vendor/lxmfy/uv.lock
@@ -0,0 +1,265 @@
+version = 1
+revision = 3
+requires-python = ">=3.11"
+
+[[package]]
+name = "cbor2"
+version = "6.1.3"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/3a/6f/07b4af8da8bd27f640362b1ac8271d80895407f2ede0c2bcc9433c06e1ca/cbor2-6.1.3.tar.gz", hash = "sha256:8d70680acb55c04ea5b5ad86da094f9612b53d5a8a65d0f5b3aafc3ce917ecbb", size = 89503, upload-time = "2026-07-04T10:36:48.793Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/f8/2e/013b2c478c41585bf5c8f9659328412eda4fe8ed30ffeb8e4fde87f8b9a3/cbor2-6.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84edab2df31c981d258d652a82a3e30eb7368d86d9d7284216282f65403a1e00", size = 421187, upload-time = "2026-07-04T10:35:54.334Z" },
+ { url = "https://files.pythonhosted.org/packages/96/78/840809f265a4537fde0ba646d92d61c500435fd811961d70776fc021b7ab/cbor2-6.1.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:932e0894476fad36b186c0da6e9b1433358bea564a60ae4799e51182568ff29f", size = 463784, upload-time = "2026-07-04T10:35:55.771Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/c5/4dad6125eea17b35ca5580a4f7308226c8a4511dfb91b94c329b167b6218/cbor2-6.1.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5f14159423a984c387982901f67313d6582251b6733c23e8bd925d73173691bf", size = 472119, upload-time = "2026-07-04T10:35:57.122Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/f7/5f0387ee7b5601c6af5277051612b8163f82ccbddbae807bbc1326754e3e/cbor2-6.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:622ec874664b4db54bc6df40f82832ad30fa5c875ad85cde84392ac62bb33d15", size = 528659, upload-time = "2026-07-04T10:35:58.496Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/7f/371ce0c200955a8a999e0a34398834ba88ef2fecda8437c3264c0673aa33/cbor2-6.1.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c9144756fa7c9298d5882d1f7ad379c4a0059803a4c70329965a000bc79bf02d", size = 538983, upload-time = "2026-07-04T10:35:59.828Z" },
+ { url = "https://files.pythonhosted.org/packages/48/d4/3cb4d40ce9bbfb41098656a0be5a8d01f24fa54cdc6d2665cbbaefbb8ba0/cbor2-6.1.3-cp311-cp311-win32.whl", hash = "sha256:144f8cfd2e9149389c34026243aeb646184cd78a2c657822be9bc9e7a2c5f3f5", size = 282264, upload-time = "2026-07-04T10:36:01.131Z" },
+ { url = "https://files.pythonhosted.org/packages/36/1f/e9d123a071ee67ebca70b37401a03b80641d86953a72e8ea41194f99095a/cbor2-6.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:187fd06befc59e6cafafc2709e5f1f3df8afe8bca5646f9cb5b70fc7e6ab1783", size = 303941, upload-time = "2026-07-04T10:36:02.45Z" },
+ { url = "https://files.pythonhosted.org/packages/32/4f/efb2ed376421641e372bfebe9fd98f11c9de3bbac1da9f2b8be5c96eb335/cbor2-6.1.3-cp311-cp311-win_arm64.whl", hash = "sha256:43f0f694f47958de50fc84e6268a3015cc2a7fce88b231456c053bc5a1c6c828", size = 296378, upload-time = "2026-07-04T10:36:03.77Z" },
+ { url = "https://files.pythonhosted.org/packages/31/16/cff14259c3d19a7f0ae88b6996fe4c85f6ff1764dad889ac8a39e843e39c/cbor2-6.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d939f55097c21e032f5a2d67592fcc57298986281f219356e2f519e4466f4ea", size = 412779, upload-time = "2026-07-04T10:36:04.975Z" },
+ { url = "https://files.pythonhosted.org/packages/50/6c/f3641d19b7b85a63cb2756c10164131489c2cb46b379ec51ae22283fefb9/cbor2-6.1.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:b025009478d644dab407164fd60e3ef4381af284f5af6966df94c663756d949e", size = 457781, upload-time = "2026-07-04T10:36:06.349Z" },
+ { url = "https://files.pythonhosted.org/packages/55/85/0c55a66f3037056bfb8e1c7184168085fdea67ae5830404498bcf466233b/cbor2-6.1.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:2226d32e102e375737656ad5d141ad8c6ae3e705e04e263f24756f0eb379c6c1", size = 468373, upload-time = "2026-07-04T10:36:07.769Z" },
+ { url = "https://files.pythonhosted.org/packages/46/74/40f7db3e0d880560193916a5c9b744fcf299558bed7113f77c28237c7c29/cbor2-6.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e61d465244d66ffed36492eef3b44d43795d76a2bba0663a2f15c186af7f7513", size = 523844, upload-time = "2026-07-04T10:36:09.404Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/a1/b5e07d6a08441c3a552fe2ae48ccb7e9dfc5065b9f6a3bae9879b4f0fbc0/cbor2-6.1.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87fe7be8fab6ec4796aa127c1a52e09e79dbafd2aa31caf809cf04b8080a5975", size = 536238, upload-time = "2026-07-04T10:36:10.914Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/99/e166be0fd74bf3a91f5a0d103e34883efbc438d970f72cc8200e274787e5/cbor2-6.1.3-cp312-cp312-win32.whl", hash = "sha256:da25d345f01e6a40b2e5c57ef96b4dcff7be69394fb62f0f70e07f437f2376a9", size = 279858, upload-time = "2026-07-04T10:36:12.247Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/1b/90b4a121e40aba189c55a5822dd3c698eaf487e1d4a780ab18c804a5ef1c/cbor2-6.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:d5514f693db6fa6f433b4096e9b604e6a7bf151c9ef1d2db86d0858e4c5e768f", size = 300929, upload-time = "2026-07-04T10:36:13.564Z" },
+ { url = "https://files.pythonhosted.org/packages/19/db/52c58a8d33464927389dde8103997b3fa51b081ce29b347ac2cc4fd0dfbf/cbor2-6.1.3-cp312-cp312-win_arm64.whl", hash = "sha256:3d43183d7beb3d3cd198d69b31bd2ee487ed704a1150c75cb0a66d6ad63d8c1a", size = 290908, upload-time = "2026-07-04T10:36:14.857Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/8c/5024d623dcf3f2057ec8c991f584b939ba5f9025a5ce8c31f6fac067137a/cbor2-6.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:21c74b8ab67977c8b87b727247eeb730145b0068ad6d47f71e9f80f6b48c65f8", size = 412334, upload-time = "2026-07-04T10:36:16.299Z" },
+ { url = "https://files.pythonhosted.org/packages/61/f3/e50654203c3b746166a96bea680eb6463b20c2c160cc14dfbe43f215ef6c/cbor2-6.1.3-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f291a0ae4c1ed96eadb0afa9752568c7424f7d6fa818676d5e33005fcd22ddd9", size = 457125, upload-time = "2026-07-04T10:36:17.684Z" },
+ { url = "https://files.pythonhosted.org/packages/44/86/6ef007f0d4f7afba90a80cb1657984de542e7474d2afaa7e920ac9860df3/cbor2-6.1.3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:dc8e44c7bf172687195dcd428157885bc00ea06efc0ea30fb371163b92bef733", size = 467651, upload-time = "2026-07-04T10:36:19.007Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/f4/b5aa27813c02f37e03eb86bd908163562edd6fc7f99665bc7bbb25ef5e6c/cbor2-6.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8ccf4d263983d830dd429d2b01f27be58ac02ba7c790c45d861f767eb63963e5", size = 523296, upload-time = "2026-07-04T10:36:20.504Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/46/e17b2bce2efdc26bfa045b4f6168f02923ac5f0e79732a1b3c42ce9ca9de/cbor2-6.1.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8719a7a2a2a82168844533389957b8f617a139f5f40e4d0ad7ed905fd3abebd1", size = 535537, upload-time = "2026-07-04T10:36:21.761Z" },
+ { url = "https://files.pythonhosted.org/packages/54/bc/add350acf37f367ae429f997f2e047b042f2d5ef9ca62461c923fbb0f3c3/cbor2-6.1.3-cp313-cp313-win32.whl", hash = "sha256:c73b54ce09dd8d522f3c1540426e36172ba0f34abf3d89eb93909a5e14590003", size = 279233, upload-time = "2026-07-04T10:36:23.071Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/6f/1bbfce3b3131e4e03e8a86966a38ff92ebb72215fcf36aeecea1547f3e4b/cbor2-6.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:b77df56c462c10eb3444db8ef78d8c3e71d9ef8d021ea92e97c1f9e3aa918690", size = 300585, upload-time = "2026-07-04T10:36:24.563Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/65/3945702dd84b6e5b7800c9c7f1ada038d33d12d0042de10e38164cc03dfd/cbor2-6.1.3-cp313-cp313-win_arm64.whl", hash = "sha256:b144be2ab3e9584ee7b6359d2a92fee0a5bec1d00dbd34c215ccc2040ac0b2ab", size = 290357, upload-time = "2026-07-04T10:36:25.983Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/40/04ad7d34182b27487a1824422b361b32d2607727ef20e563056eba62d12a/cbor2-6.1.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3f3167ca9920b90db4ff72652db109dfd93b56ee0d583aca12f3a5d9a7019477", size = 414615, upload-time = "2026-07-04T10:36:27.299Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/29/94238c61f90653a606535e7509a2af312089fe10dafcb4cf82d6905a7a1a/cbor2-6.1.3-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:48c677971e4b71e685491e1a267d9924dc205e7ddbe3f34fd2562f16c0f6bfca", size = 459084, upload-time = "2026-07-04T10:36:28.717Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/cb/6bd33461e8be8ded7ebb0fa38994a63752aefae2b4fcd1b2cc71ee3c06f1/cbor2-6.1.3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ad4f3c6dfc6b83331eb04c6975efb2839ab65a3aa81502bc2b3f7945d4c4aa44", size = 469310, upload-time = "2026-07-04T10:36:30.136Z" },
+ { url = "https://files.pythonhosted.org/packages/8e/d1/94195bcd8fcc1030ecaf22a7a825faa08891b5bb2d3553e465e50fe115f5/cbor2-6.1.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8bcd609eab4a39745123bfb28e73311abba3d14975a87f5906e0e8b910d918ac", size = 524287, upload-time = "2026-07-04T10:36:31.453Z" },
+ { url = "https://files.pythonhosted.org/packages/06/55/57178fbf2d1206af5299c688f9c917b83f30636694c8f932dc89c6652545/cbor2-6.1.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:672727ecb27d7fb3ca0bf8a58fc489d5374ab1fee680ed3d0348a11d9d3ca78f", size = 537031, upload-time = "2026-07-04T10:36:32.769Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/01/beefe26258d66ce39a9b057b679bc67dba81698d5a63e0ada5b4752a5c87/cbor2-6.1.3-cp314-cp314-win32.whl", hash = "sha256:1413cef2aa7f478a38298cd3492a055e8e8e45d17fb53bbe103e79ca15c33f3c", size = 286256, upload-time = "2026-07-04T10:36:34.078Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/2d/79eb513a2586a2053a6f18b33693beda65e2c766820d99676a306573fed5/cbor2-6.1.3-cp314-cp314-win_amd64.whl", hash = "sha256:59df264d4a508ba61daaa0bf3c2f92d63275509549a0875c1fa38176f651e4f8", size = 313744, upload-time = "2026-07-04T10:36:35.58Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/c7/ab9828e4efc26badf89f1397f866f3ef03ef65cdcd55518254b84bdaf86e/cbor2-6.1.3-cp314-cp314-win_arm64.whl", hash = "sha256:b62b5d80a0eb4305cd5f0217faa4d7747bd64fe0dff9b88415e7be3782f8249b", size = 304280, upload-time = "2026-07-04T10:36:36.865Z" },
+ { url = "https://files.pythonhosted.org/packages/33/cf/54a497ad1026833c1c92d482edbda0bdebb48314b51563d2fcea24ab89b4/cbor2-6.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:856ab525bfc599588b8d2a45babb7c3400c693ea0bb574d818467d997102fe24", size = 409638, upload-time = "2026-07-04T10:36:38.089Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/03/efdecd0848b9c9e43242537f6dd8ac5d441a077d362e5e6954c7775b866a/cbor2-6.1.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a076f35abf1dd0a4de6e2f7f4d4932abafc951a26275b6aa4a3b370c2fd3bbf4", size = 452193, upload-time = "2026-07-04T10:36:39.56Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/bf/b5f43c75dc5f0ca3127919d3273d8671917932aa3e90767da63867e0f06f/cbor2-6.1.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:37ccab1d0bd3f57ff536a41e44165d0c99cb166ac6e5ffb8b93c42304b56e48d", size = 466614, upload-time = "2026-07-04T10:36:40.936Z" },
+ { url = "https://files.pythonhosted.org/packages/23/ee/e85b2ddd46b3b43e39a986704353b433efab0881234e1b4d824229fc2a75/cbor2-6.1.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76d257ce797e651fa430b0269e8e8c43549c54ce8b0d12860569b3709bf1326f", size = 518503, upload-time = "2026-07-04T10:36:42.289Z" },
+ { url = "https://files.pythonhosted.org/packages/60/13/c740c0002f127dc3e9e87b61a5d1285dd3b71aa11d8e0f6a408fc1f36173/cbor2-6.1.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4af35baadb66f7c9cbb3998eab469767c04641552416c1c69dd4d3d183797119", size = 534236, upload-time = "2026-07-04T10:36:43.641Z" },
+ { url = "https://files.pythonhosted.org/packages/73/cd/a57d97177f3777c96f3406efb0ad60794fdbf249d497ec24559bfd1d0328/cbor2-6.1.3-cp314-cp314t-win32.whl", hash = "sha256:61c92661665bccfed4ffa69d1fe10097f2c820d262f56a8cf909a5ebf9f6d8c6", size = 282446, upload-time = "2026-07-04T10:36:44.888Z" },
+ { url = "https://files.pythonhosted.org/packages/11/c8/dd54878589df22c863d526cb82e1bb20e953d40223436449a52481c49804/cbor2-6.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:dc6bcd030bf5043662b84b0ca0f0ca942491bf509105db30cedca6e2ce82d158", size = 310300, upload-time = "2026-07-04T10:36:46.212Z" },
+ { url = "https://files.pythonhosted.org/packages/05/d0/b0780a396d145e3356bfdc48578d021ade1818a6c7d7842a3d6bc6f16fbb/cbor2-6.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:da98a5e0ae9487bed497ac74e2c850b49975a3b7b5314b76c3843e2c83a6c8c4", size = 299391, upload-time = "2026-07-04T10:36:47.629Z" },
+]
+
+[[package]]
+name = "cffi"
+version = "2.1.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d3/67/85c89a59ba36a671e79638f44d466749f08179266a57e4f2ffdf92174072/cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc", size = 183845, upload-time = "2026-07-06T21:32:26.32Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/dd/e3b0baa2d3d6a857ac72b7efbf18e32e487c9cdafcc13049ad765495b15e/cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7", size = 184186, upload-time = "2026-07-06T21:32:28.025Z" },
+ { url = "https://files.pythonhosted.org/packages/65/68/9f3ef890cf3c6ab97bd531c5677f67613d302165d16f8142b2811782a614/cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93", size = 211892, upload-time = "2026-07-06T21:32:29.565Z" },
+ { url = "https://files.pythonhosted.org/packages/22/d7/1a74539db16d8bfd839ff1515948948efbb162e574650fd3d846896eea95/cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2", size = 218793, upload-time = "2026-07-06T21:32:30.951Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/d1/9a5b7169499e8e8d8e636de70b97ac7c9447104d2ff1a2cd94790cea5162/cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c", size = 205737, upload-time = "2026-07-06T21:32:32.216Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/b0/e131a9c41f10607926278453d9596163594fe1c4ebc46efe3b5e5b34eb84/cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f", size = 204909, upload-time = "2026-07-06T21:32:33.655Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/d2/4398416cd699b35167947c6e22aca52c47e69ad5695073c9f1f2c52e04aa/cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565", size = 217883, upload-time = "2026-07-06T21:32:35.173Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/a5/d4fe77b589e5e82d43ebc809bf2e6474afe8e48e32ea050b9357645b6471/cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c", size = 221251, upload-time = "2026-07-06T21:32:36.527Z" },
+ { url = "https://files.pythonhosted.org/packages/22/f0/a2fc43084c0433caf7f461bccc013e28f848d04ee1c5ed7fce71423cf4d9/cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02", size = 214250, upload-time = "2026-07-06T21:32:37.852Z" },
+ { url = "https://files.pythonhosted.org/packages/04/8c/b925975448cf20634a9fbd5efceb807219db452653648d2897c0989cab2d/cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e", size = 219441, upload-time = "2026-07-06T21:32:39.146Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/da/5c4918a2d61d86fa927d716cb3d8e4626ef8dc8f605a599d32f33897f59a/cffi-2.1.0-cp311-cp311-win32.whl", hash = "sha256:64c753a0f87a256020004f37a1c8c02c480e725f910f0b2a0f3f07debd1b2479", size = 174496, upload-time = "2026-07-06T21:32:40.467Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/c8/6c2de1d55cf35ef8b92885d5ef280790f0fb9634d87ea1cc315176aecd61/cffi-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:4f26194e3d95e06501b942642855aed4f953d55e95d7d01b7c4483db3ecff458", size = 185113, upload-time = "2026-07-06T21:32:41.761Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/4e/e8d7cb5783f1841a3c8fb3a7735838d7484d08ec08c9f984b14cac1ac0e9/cffi-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:35aaea0c7ee0e58a5cd8c2fd1a48fdf7ece0d2699b7ecdda08194e9ce5dd9b3d", size = 179927, upload-time = "2026-07-06T21:32:42.961Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/85/990925db5df586ec90beb97529c853497e7f85ba0234830447faf41c3057/cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f", size = 184829, upload-time = "2026-07-06T21:32:44.324Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/92/e7bb136ad6b5352603732cf907ef862ca103f20f2031c1735a46300c20c9/cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde", size = 184728, upload-time = "2026-07-06T21:32:45.683Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/c0/d1ec30ffb370f748f2fb54425972bfef9871e0132e82fb589c46b6676049/cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d", size = 214815, upload-time = "2026-07-06T21:32:48.557Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/dc/5620cf930688be01f2d673804291de757a934c90b946dbdc3d84130c2ea4/cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7", size = 222429, upload-time = "2026-07-06T21:32:49.848Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/a4/77b53abbf7a1e0beb9637edbef2a94d15f9c822f591e85d439ffd91519a6/cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b", size = 210315, upload-time = "2026-07-06T21:32:51.221Z" },
+ { url = "https://files.pythonhosted.org/packages/58/0c/f528df19cc94b675087324d4760d9e6d5bfae97d6217aa4fac43de4f5fcc/cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7", size = 208859, upload-time = "2026-07-06T21:32:52.512Z" },
+ { url = "https://files.pythonhosted.org/packages/62/f2/c9522a81c32132799a1972c39f5c5f8b4c8b9f00488a23feaa6c06f07741/cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66", size = 221844, upload-time = "2026-07-06T21:32:53.704Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/28/bd53988b9833e8f8ad539d26f4c07a6b3f6bcb1e9e02e7ca038250b3428d/cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe", size = 225287, upload-time = "2026-07-06T21:32:54.907Z" },
+ { url = "https://files.pythonhosted.org/packages/79/99/0d0fd37f055224085f42bbb2c022d002e17dde4a97972822327b07d84101/cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b", size = 223681, upload-time = "2026-07-06T21:32:56.329Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/80/c138990aa2a70b1a269f6e06348729836d733d6f970867943f61d367f8cc/cffi-2.1.0-cp312-cp312-win32.whl", hash = "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a", size = 175269, upload-time = "2026-07-06T21:32:57.777Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/eb/f636456ff21a83fc13c032b58cc5dde061691546ac79efa284b2989b7982/cffi-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384", size = 185881, upload-time = "2026-07-06T21:32:59.253Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/2c/400ea43e721727dca8a65c4521390e9196757caba4a45643acb2b63271b8/cffi-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6", size = 180088, upload-time = "2026-07-06T21:33:02.278Z" },
+ { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" },
+ { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" },
+ { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" },
+ { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" },
+ { url = "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d", size = 184965, upload-time = "2026-07-06T21:33:26.605Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac", size = 184952, upload-time = "2026-07-06T21:33:27.823Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913", size = 210051, upload-time = "2026-07-06T21:33:30.534Z" },
+ { url = "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d", size = 208630, upload-time = "2026-07-06T21:33:31.753Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd", size = 177723, upload-time = "2026-07-06T21:33:51.626Z" },
+ { url = "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", size = 187937, upload-time = "2026-07-06T21:33:53.403Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804", size = 183001, upload-time = "2026-07-06T21:33:54.74Z" },
+ { url = "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714", size = 188538, upload-time = "2026-07-06T21:33:36.792Z" },
+ { url = "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376", size = 188230, upload-time = "2026-07-06T21:33:38.011Z" },
+ { url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13", size = 211652, upload-time = "2026-07-06T21:33:40.851Z" },
+ { url = "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d", size = 210755, upload-time = "2026-07-06T21:33:42.183Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" },
+ { url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76", size = 182857, upload-time = "2026-07-06T21:33:47.696Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5", size = 194065, upload-time = "2026-07-06T21:33:48.953Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8", size = 186404, upload-time = "2026-07-06T21:33:50.309Z" },
+ { url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" },
+ { url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3", size = 184936, upload-time = "2026-07-06T21:33:58.765Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc", size = 185045, upload-time = "2026-07-06T21:34:00.085Z" },
+ { url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022", size = 210073, upload-time = "2026-07-06T21:34:03.255Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0", size = 208551, upload-time = "2026-07-06T21:34:04.433Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6", size = 177696, upload-time = "2026-07-06T21:34:26.355Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853", size = 187914, upload-time = "2026-07-06T21:34:27.58Z" },
+ { url = "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda", size = 183004, upload-time = "2026-07-06T21:34:28.905Z" },
+ { url = "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc", size = 188378, upload-time = "2026-07-06T21:34:09.926Z" },
+ { url = "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca", size = 188319, upload-time = "2026-07-06T21:34:11.101Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8", size = 211554, upload-time = "2026-07-06T21:34:13.987Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd", size = 210795, upload-time = "2026-07-06T21:34:15.972Z" },
+ { url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" },
+ { url = "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b", size = 182760, upload-time = "2026-07-06T21:34:22.059Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5", size = 193769, upload-time = "2026-07-06T21:34:23.589Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" },
+]
+
+[[package]]
+name = "cryptography"
+version = "49.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" },
+ { url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" },
+ { url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" },
+ { url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" },
+ { url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/9e/db72b3ae7fc9cfad53e630e56c6ae83b9b6ff0bf3718ffb8012d20b3aabf/cryptography-49.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:73a205dce83953d131a4aa1e0fd917a2fd1c5b1eef251e9d7152efefcbf5caf7", size = 4013892, upload-time = "2026-06-12T20:02:10.735Z" },
+ { url = "https://files.pythonhosted.org/packages/86/12/c48a424f38db03027be9f7ed5c7dc5de9933dbee992865f98b13727a009d/cryptography-49.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:196ecd6a36e4e9aa10270393bb98d8df88fccee0bf1e5128b91ae4eb4375896d", size = 4678835, upload-time = "2026-06-12T20:02:48.743Z" },
+ { url = "https://files.pythonhosted.org/packages/68/28/8a3ad4653662c93fc44dc4e5d8fd374c25c42e07b34bbfbadf49cf57a5a8/cryptography-49.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7abcee80084cda3f7691f3eb1ce480d8df49cec637b429aa35986c1de71738aa", size = 4697239, upload-time = "2026-06-12T20:02:56.03Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/b2/2193fc74f81aee4f9b62733133b73b5176718932ed8f2e4b03fa040480a6/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:4ae387c9cb68ea569ca17e490d66d8142b81c3cc814bf179974b7d146e490bbb", size = 4685593, upload-time = "2026-06-12T20:02:50.666Z" },
+ { url = "https://files.pythonhosted.org/packages/47/f1/1d3eaa243bfc5de4a187b22aa8c048b3e4980bfbe830ac46e6bac2e66947/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:f37d847238971164fdbc68ade6f6574aecc9c0af714190e2083429ff68f4ce9d", size = 5289961, upload-time = "2026-06-12T20:01:46.468Z" },
+ { url = "https://files.pythonhosted.org/packages/58/39/2d51306721330c486495853eda1c567880ff036de15a14c4b74f399934af/cryptography-49.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:c2bc30226390d60ea19d9f82b19db005fe0452154a23c1c410c12ea801e43561", size = 4731145, upload-time = "2026-06-12T20:02:16.832Z" },
+ { url = "https://files.pythonhosted.org/packages/17/50/983e838c7fd0d87fd8c969bcdd328edaf5f756e38df5281637424c155873/cryptography-49.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:07cab27cc7b7e0fd28e5e26bb9eeedde5c135c868b46de4a27845abe94af6122", size = 4321719, upload-time = "2026-06-12T20:02:52.611Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/f5/8f571d7e27c55bce9f76f026143bcb1e040a4233149ecca0bea5fa5dd5f7/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:b20133d204d2bb56ba047642199603876c872026ca53e79c35b83772ab2cc505", size = 4685209, upload-time = "2026-06-12T20:02:07.282Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/84/0e27016a6fc5a0886f797018b26aa42f40c09a82332bff77822a451deaaa/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b970c6da94d5bb18629db453d14f2a1300f6bf59b61e9b82377931ef95504866", size = 5246285, upload-time = "2026-06-12T20:01:32.439Z" },
+ { url = "https://files.pythonhosted.org/packages/11/2d/5e1fb307cb5931881516b464c98774b3f2c36b5d4bb9a2830253cf553cad/cryptography-49.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:d8ecde755e2e91bf773fc94e8c9d730cd7f2007004cb492263a794ec3899a1c8", size = 4730441, upload-time = "2026-06-12T20:02:01.469Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/c0/bff5a02ee731d207d6a1ed51732549d8c53d2bc8da1d10ec6f2844201d68/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e3fb64c420688e5319ae25113a354015abbd8dffbfbc41781a1ea66fc7622ac3", size = 4815869, upload-time = "2026-06-12T20:01:36.574Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/26/814681d14248d95d73d5c3eea0c39a94eb8302df966f670a2c60de90974b/cryptography-49.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32703d93296f5c1f4b53349ad3a250c2cae0fdecd3a3dd5d47e616d8d616af27", size = 4960948, upload-time = "2026-06-12T20:02:18.688Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/fe/93ecac273d3738939d023612ad12cca9a3740a5345d69fda04134c43fd96/cryptography-49.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:33cd0565932807baddb67b96dbee92f2c374b5c89dee09fd74079aeb8c8dba61", size = 3799153, upload-time = "2026-06-12T20:01:39.059Z" },
+ { url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" },
+ { url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" },
+ { url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" },
+ { url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" },
+ { url = "https://files.pythonhosted.org/packages/63/d3/4a83af35d65e3fad632c926fad684c193ea4398569ccb0bbbc7fe8f5dc9a/cryptography-49.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b", size = 3993685, upload-time = "2026-06-12T20:02:14.883Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/a7/f9dac0ab7f80368c56993a7bf638ef9935f825c91902798481fac0898138/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838", size = 4676239, upload-time = "2026-06-12T20:02:28.793Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/70/2ba3769dd0ae167e2f33dfa9592d45db6ff9a61d62ca1a5b3d1bdd09068f/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5", size = 4715584, upload-time = "2026-06-12T20:01:27.495Z" },
+ { url = "https://files.pythonhosted.org/packages/94/64/2923570ac1c0bd3a737aa366ac3abbbbde273042308b8cde95e2364a6e6a/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615", size = 4675885, upload-time = "2026-06-12T20:01:55.49Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/f8/614dc7e051418cfe53d55173c1e24c6b0085e89996fe90508c2fdf769aef/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6", size = 4715449, upload-time = "2026-06-12T20:02:05.469Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/50/a9caea39ad19c431c1a3f8a31114df65b260cdfe67786b6c7e7c040c4c44/cryptography-49.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6", size = 3783731, upload-time = "2026-06-12T20:02:43.319Z" },
+]
+
+[[package]]
+name = "lxmf"
+version = "1.0.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "rns" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/33/2b/b1df90fce728db3a0659da44bfa8f1337670ccc6a937b7a988c119a4b05c/lxmf-1.0.1.tar.gz", hash = "sha256:d12ead448296cbd09203462d8dc96a26eb71ef168b4197b133a970d483a607ea", size = 71315, upload-time = "2026-06-01T12:26:07.874Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/86/ff/4374bc1ed3a6b5e3d4c9c9e40793b7ecf77998d3a41eae66501155aac8f3/lxmf-1.0.1-py3-none-any.whl", hash = "sha256:96fbf9a02dcd311fd31129724c08faa5f42a0e4e1bab63bea7552fbc983f4fb8", size = 64470, upload-time = "2026-06-01T12:26:06.044Z" },
+]
+
+[[package]]
+name = "lxmfy"
+version = "2.0.1"
+source = { editable = "." }
+dependencies = [
+ { name = "cbor2" },
+ { name = "lxmf" },
+ { name = "rns" },
+]
+
+[package.metadata]
+requires-dist = [
+ { name = "cbor2", specifier = ">=5.4.0" },
+ { name = "lxmf", specifier = ">=1.0.1" },
+ { name = "rns", specifier = ">=1.3.9" },
+]
+
+[[package]]
+name = "pycparser"
+version = "3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
+]
+
+[[package]]
+name = "pyserial"
+version = "3.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1e/7d/ae3f0a63f41e4d2f6cb66a5b57197850f919f59e558159a4dd3a818f5082/pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb", size = 159125, upload-time = "2020-11-23T03:59:15.045Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/07/bc/587a445451b253b285629263eb51c2d8e9bcea4fc97826266d186f96f558/pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", size = 90585, upload-time = "2020-11-23T03:59:13.41Z" },
+]
+
+[[package]]
+name = "rns"
+version = "1.4.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "cryptography" },
+ { name = "pyserial" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/3c/45/5aaed5107e0d3f434aada725224e1c54af96555959da312c01e12228c1de/rns-1.4.0.tar.gz", hash = "sha256:fa9e76d0a78bf253eae66137e6bbdc65f470db3950a95b034ce32ca845ff0e44", size = 519727, upload-time = "2026-07-20T17:13:03.624Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/56/4e/1a50351fba424b891a61e5f0fed9f3f631d7a034ae5edcc531d79562467c/rns-1.4.0-py3-none-any.whl", hash = "sha256:a88c6ae15c289867b2d8ee8f6ba4363f4dc728283f892b0790dba226f7dddcae", size = 606736, upload-time = "2026-07-20T17:12:58.403Z" },
+]
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────